 //    
//       KOC POWER ALL - EMH EDITION                                    
//       PLUGIN: BUTTLER                                            
//       VERSION: 09.07.2013                                        
//       AUTHOR: EMH                                                
//      


ksxVersion = "09.07.2013";
ksxEnabled = true;
ksxDebug = false;



//#region Main

//DEBUGCODE for finding the proper page
//if(ksxDebug) GM_log("Loading document: " +
//    "id=" + document.getElementsByTagName("html")[0].getAttribute("id") + " title: " + document.title + 
//    " \n\t" + "href: " + document.location.href);

// @include       *.kingdomsofcamelot.com/fb/e2/src/main_src.php*
if (ksxEnabled && document.body.id == 'mainbody') {
    //    /*DEBUGCODE:*/if(ksxDebug) GM_log("Setup KoC Buttler \n" + "location: " + document.location.href);

    ksx = uW.ksx = new KsxLibrary();
    //ksx.koc.lang = ksx.koc.getLanguage();
    ksx.koc.lang = LangOptions.culang;
    ksx.compatibility.disableMarchXYPaste = false; // [Attack]

    //GM_log("INFO: language " + ksx.koc.lang + " / " + culang.ksx_KoCButtler_language);
    //special configuration removed into plugin
    
    // Toolbar Configuration
    ksx.configuration.toolbar = {items: []};
    ksx.configuration.toolbar.items.push(["Button", , culang.ksx_KoCButtler_Toolbar_NewMessage, "ksx.plugins.Toolbar.macros.newMessage(); return false;"]);
    ksx.configuration.toolbar.items.push(["Button", , culang.ksx_KoCButtler_Toolbar_MyReports, "modal_messages();track_chrome_btn('messages_btn');Messages.listReports();return false;"]);
    //ksx.configuration.toolbar.items.push(["Button", , "R", "ksx.tools.click('//*[@id=\"pbtcBerichte\"]/a'); return false;"]);
    ksx.configuration.toolbar.items.push(["Button", , culang.ksx_KoCButtler_Toolbar_AllianceReports, "modal_alliance();track_chrome_btn('alliance_btn');allianceReports();modal_alliance_changetab(4);return false;"]);
    //             ["Button", , "Einladung",  "modal_alliance();track_chrome_btn('alliance_btn');inviteFriendsModule();return  false;"],
    //NOTWORKING["Button",  , "Einladung",  "modal_alliance();track_chrome_btn('alliance_btn');inviteFriendsModule();WAIT;searchTabInAllianceInvite();return  false;"],
    //            ["LineBreak"],    
    ksx.configuration.toolbar.items.push(["Separator", ]);
    //            ["Button",, "Truppen aussenden", "modal_attack(4,'','');return false;"],
    //             ["Button", , "Sp?h", "ksx.plugins.Toolbar.macros.newMarch(3,  '','', [0,0,1,0,0,0,0,0,0,0,0,0], [0,0,0,0,0] );return false;"],
    ksx.configuration.toolbar.items.push(["Button", , culang.mainReinforce, "ksx.plugins.Toolbar.macros.newMarch(2, '','', [0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0] );return false;"]);
    ksx.configuration.toolbar.items.push(["Button", , culang.mainReassign, "ksx.plugins.Toolbar.macros.newMarch(5, '','', [0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0] );return false;"]);
    ksx.configuration.toolbar.items.push(["Button", , culang.mainTransport, "ksx.plugins.Toolbar.macros.newMarch(1, '','', [0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0] );return false;"]);
    ksx.configuration.toolbar.items.push(["Button", "ksxMarchExtenderRepeatOpen", "Repeat", ""]);
    //            ["Button","ksxMarchExtenderRepeatMarch", "Wiederholen!"     , ""],
    //ksx.configuration.toolbar.items.push(["Button",,  "Dump Seed"        , "ksx.plugins.Toolbar.macros.dumpSeed(); return  false;"]);
    //ksx.configuration.toolbar.items.push(["Button",,  "Dump Throne"        , "ksx.plugins.Toolbar.macros.dumpThroneItems();  return false;"]);
    
    ksx.configuration.powertoolbar = {items: [], syncButtons: []};
    ksx.configuration.powertoolbar.items.push(["Button", "ksxpbTraderState", culang.mainTransport+" ${[x]}", ""]);
    ksx.configuration.powertoolbar.items.push(["Button", "ksxpdxSupplyState", culang.shrsupply+" ${[x]}", ""]);
    ksx.configuration.powertoolbar.items.push(["Button", "ksxpbReassignState", culang.tabLabelReassign+" ${[x]}", ""]);
    ksx.configuration.powertoolbar.items.push(["Separator"]);
    ksx.configuration.powertoolbar.items.push(["Button", "ksxpbCrest", culang.tabLabelCrest+" ${[x]}", ""]);
    ksx.configuration.powertoolbar.items.push(["Button", "ksxpbDarkForest", culang.mainDarkForest+" ${[x]}", ""]);
    ksx.configuration.powertoolbar.items.push(["Separator"]);
    ksx.configuration.powertoolbar.items.push(["Button", "ksxpbRaidStart", culang.raidAutoReset+" ${[x]}", ""]);
    ksx.configuration.powertoolbar.items.push(["Button", "ksxpbEveryEnable", culang.ksx_KoCButtler_PowerToolbar_autoRefresh+" ${[x]}", ""]);
    ksx.configuration.powertoolbar.items.push(["LineBreak"]);
    ksx.configuration.powertoolbar.items.push(["Button", "ksxpbBuildRunning", culang.buildButton+" ${[x]}", ""]);
    ksx.configuration.powertoolbar.items.push(["Button", "ksxpbBuildMode", culang.buildMode+" ${[x]}", ""]);
//    ksx.configuration.powertoolbar.items.push(["Button", "ksxpbHelpRequest", culang.buildAskForHelp+" ${[x]}", ""]);
    ksx.configuration.powertoolbar.items.push(["Separator"]);
    ksx.configuration.powertoolbar.items.push(["Button", "ksxpbAutoTrain", culang.buttonAutoTrain+" ${[x]}", ""]);    
    ksx.configuration.powertoolbar.items.push(["Button", "ksxpbCrafting", culang.craftButton+" ${[x]}", ""]);
    ksx.configuration.powertoolbar.items.push(["Button", "ksxpbsalvage_run", culang.throneStuffAutoSalvage+" ${[x]}", ""]);
    ksx.configuration.powertoolbar.items.push(["Separator"]);
    ksx.configuration.powertoolbar.items.push(["Button", "ksxpbAttackSet", "T-Set " + culang.mainAttack + " ${[x]}", ""]); //16.10.2012 Bommel
    ksx.configuration.powertoolbar.items.push(["Button", "ksxpbRestoreSet", "T-Set " + culang.mainEnd + " ${[x]}", ""]); //16.10.2012 Bommel
    ksx.configuration.powertoolbar.items.push(["LineBreak"]);
    ksx.configuration.powertoolbar.items.push(["Button", "ksxpbWildAttack", culang.mainWildAttack+" ${[x]}", ""]);
    ksx.configuration.powertoolbar.items.push(["Button", "ksxpbAttack", culang.mainAttack+" ${[x]}", ""]);
    // A): ["{ToolbarButtonID}" , "{PowerButtonID}" , "value"            , "{TrueCondition}" ] :: for a button
    // A): ["{ToolbarButtonID}" , "{PowerButtonID}" , "checked"          , "true"            ] :: for a checkbox
    // B): ["{ToolbarButtonID}" , "P:{StatePath}"   , "M:{ToggleMethod}" , ""                ] :: code direct
 ksx.configuration.powertoolbar.syncButtons = [
        // ["ksxpbTraderState"  , "pbTraderState", "value", " = " +culang.buttonon], //obsolete method to sync
        ["ksxpbTraderState"  , "P:Tabs.Transport.traderState.running", "M:Tabs.Transport.toggleTraderState()", ""],
        ["ksxpdxSupplyState"  , "P:Tabs.Transport.reapproState.running", "M:Tabs.Transport.toggleReapproState()", ""],
        ["ksxpbReassignState", "P:Tabs.Reassign.reassignState.running", "M:Tabs.Reassign.toggleReassignState()", ""],
        ["ksxpbBuildRunning", "pbBuildRunning", "value", " = "+culang.buttonON],
        ["ksxpbBuildMode", "pbBuildMode", "value", " = "+culang.buttonON],
        ["ksxpbRaidStart", "pbRaidStart", "value", " = "+culang.buttonON],
        ["ksxpbAutoTrain", "P:TrainOptions.Running", "M:Tabs.AutoTrain.toggleAutoTrainState()", ""],
        ["ksxpbCrafting", "P:Tabs.AutoCraft.options.running", "M:Tabs.AutoCraft.toggleCraftStateRunning()", ""],
//        ["ksxpbCrest", "Cresttoggle", "value", " = "+culang.buttonON],
        ["ksxpbCrest", "P:Options.crestRunning", "M:Tabs.Crest.toggleCrestState()", ""],
        ["ksxpbDarkForest", "AttSearch", "value", " = "+culang.buttonON],
        ["ksxpbHelpRequest"  , "pbHelpRequest", "checked","true"],
        ["ksxpbEveryEnable", "pbEveryEnable", "checked", "true"],
 //!?!     ["ksxpbEveryEnable", "P:Options.pbEveryEnable",  "M:if(Options.pbEveryEnable ){Options.pbEveryEnable=false;}else  {Options.pbEveryEnable=true;}  RefreshEvery.setEnable(Options.pbEveryEnable) ", ""],
        ["ksxpbsalvage_run"  , "P:Options.ThroneDeleteItems", "M:Tabs.ThroneStuff.toggleSalvage()", ""],
        ["ksxpbAttackSet"  , "P:Options.alertConfig.changeThrone", "M:changeThrone()", ""],
        ["ksxpbRestoreSet"  , "P:Options.alertConfig.returnThrone", "M:returnThrone()", ""],
        ["ksxpbMerc", "P:Options.AttackRunning", "M:Tabs.DarkForest.toggleBarbState()", ""],
        ["ksxpbWildAttack", "P:WildOptions.Running", "M:Tabs.WildAttack.toggleWildState()", ""],
        ["ksxpbAttack", "P:AtkOptions.Running", "M:Tabs.Attack.toggleAttackState()", ""],
    ]
    
    // Chat Configuration
    ksx.configuration.chat = {
        enableTimeCorrection: true,
        backgrounds: {
            RequestItemBackground: 'lightgreen',
            OwnItemBackground    : 'lightgray',
            InfoBackground       : "#E0E060",   //Colors.SpamBackground,
            //AllyAttackBackground : "#FFB0E0",
            //OwnAttackBackground  : "#FF8080"
        }
    };
    
    //if (Options.DeskInfo       ) ksx.plugins.DeskInfo        = new DeskInfoPlugin(ksx);              
    ksx.plugins.Toolbar         = new ToolbarPlugin(ksx);
    ksx.plugins.PowerToolbar    = new PowerToolbarPlugin(ksx);
    ksx.plugins.ChatMonitor     = new ChatMonitorPlugin(ksx);
    ksx.plugins.ResourceRequest = new ResourceRequestPlugin(ksx); //requires ChatMonitor
    ksx.plugins.MarchExtender   = new MarchExtenderPlugin(ksx);   //requires Toolbar
    ksx.plugins.Reports         = new ReportsPlugin(ksx);         //requires Toolbar    

    // ### Crest Restart Workaround ###
    // ERR: sometimes new crest marches are not startet, may be because timeout (march returns) or whatever
    // FIX: if crest is ON it will toggle OFF/ON all 5 minutes, this will restart the crest function
    if(false) setInterval(function(){ 
        //GM_log("Crest Restart Workaround")
        if(!Options.crestRunning) return;
        //GM_log("Crest Restart Workaround Toggle OFF")
        Tabs.Crest.toggleCrestState();
        setTimeout(function(){ 
            if(Options.crestRunning) return;
            //GM_log("Crest Restart Workaround Toggle ON")
            Tabs.Crest.toggleCrestState();
        },500);
    }, 5*60*1000);
    
    // ### XY paste on map
    if (!ksx.compatibility.disableMarchXYPaste)
        ksx.koc.attachXYPaste('mapXCoor', 'mapYCoor');
}
//#endregion
function changeThrone() {
    if (Options.alertConfig.changeThrone) {
        Options.alertConfig.changeThrone = false;
        ById('Ksalertthrone').checked = false;
        Options.alertConfig.returnThrone = false;
        ById('KsalertthroneRet').checked = false;
        Options.alertConfig.RecentActivity = false;
    } else {
        Options.alertConfig.changeThrone = true;
        ById('Ksalertthrone').checked = true;
    }
    saveOptions();
}
function returnThrone() {
    if (Options.alertConfig.returnThrone) {
        Options.alertConfig.returnThrone = false;
        ById('KsalertthroneRet').checked = false;
        Options.alertConfig.RecentActivity = false;
    } else {
        Options.alertConfig.returnThrone = true;
        ById('KsalertthroneRet').checked = true;
    }
    saveOptions();
}



function KsxLibrary() {
    if(ksxDebug) GM_log(new Date().toTimeString().substring(0, 8) + '.' + new Date().getMilliseconds() + ': ' + "KsxLibrary");
    
    //var  JSON= {}; (function () { "use strict"; function f(n) { return n < 10  ? '0' + n : n; } if (typeof Date.prototype.toJSON !== 'function') {  Date.prototype.toJSON = function (key) { return isFinite(this.valueOf())  ? this.getUTCFullYear() + '-' + f(this.getUTCMonth() + 1) + '-' +  f(this.getUTCDate()) + 'T' + f(this.getUTCHours()) + ':' +  f(this.getUTCMinutes()) + ':' + f(this.getUTCSeconds()) + 'Z' : null; };  String.prototype.toJSON = Number.prototype.toJSON =  Boolean.prototype.toJSON = function (key) { return this.valueOf(); }; }  var cx =  /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,  escapable =  /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,  gap, indent, meta = { '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f':  '\\f', '\r': '\\r', '"': '\\"', '\\': '\\\\' }, rep; function  quote(string) { escapable.lastIndex = 0; return escapable.test(string) ?  '"' + string.replace(escapable, function (a) { var c = meta[a]; return  typeof c === 'string' ? c : '\\u' + ('0000' +  a.charCodeAt(0).toString(16)).slice(-4); }) + '"' : '"' + string + '"'; }  function str(key, holder) { var i, k, v, length, mind = gap, partial,  value = holder[key]; if (value && typeof value === 'object'  && typeof value.toJSON === 'function') { value =  value.toJSON(key); } if (typeof rep === 'function') { value =  rep.call(holder, key, value); } switch (typeof value) { case 'string':  return quote(value); case 'number': return isFinite(value) ?  String(value) : 'null'; case 'boolean': case 'null': return  String(value); case 'object': if (!value) { return 'null'; } gap +=  indent; partial = []; if (Object.prototype.toString.apply(value) ===  '[object Array]') { length = value.length; for (i = 0; i < length; i  += 1) { partial[i] = str(i, value) || 'null'; } v = partial.length === 0  ? '[]' : gap ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind +  ']' : '[' + partial.join(',') + ']'; gap = mind; return v; } if (rep  && typeof rep === 'object') { length = rep.length; for (i = 0; i  < length; i += 1) { k = rep[i]; if (typeof k === 'string') { v =  str(k, value); if (v) { partial.push(quote(k) + (gap ? ': ' : ':') + v);  } } } } else { for (k in value) { if (Object.hasOwnProperty.call(value,  k)) { v = str(k, value); if (v) { partial.push(quote(k) + (gap ? ': ' :  ':') + v); } } } } v = partial.length === 0 ? '{}' : gap ? '{\n' + gap +  partial.join(',\n' + gap) + '\n' + mind + '}' : '{' + partial.join(',')  + '}'; gap = mind; return v; } } if (typeof JSON.stringify !==  'function') { JSON.stringify = function (value, replacer, space) { var  i; gap = ''; indent = ''; if (typeof space === 'number') { for (i = 0; i  < space; i += 1) { indent += ' '; } } else if (typeof space ===  'string') { indent = space; } rep = replacer; if (replacer &&  typeof replacer !== 'function' && (typeof replacer !== 'object'  || typeof replacer.length !== 'number')) { throw new  Error('JSON.stringify'); } return str('', { '': value }); }; } if  (typeof JSON.parse !== 'function') { JSON.parse = function (text,  reviver) { var j; function walk(holder, key) { var k, v, value =  holder[key]; if (value && typeof value === 'object') { for (k in  value) { if (Object.hasOwnProperty.call(value, k)) { v = walk(value,  k); if (v !== undefined) { value[k] = v; } else { delete value[k]; } } }  } return reviver.call(holder, key, value); } text = String(text);  cx.lastIndex = 0; if (cx.test(text)) { text = text.replace(cx, function  (a) { return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);  }); } if  (/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,   '@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,  ']').replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { j = eval('(' + text +  ')'); return typeof reviver === 'function' ? walk({ '': j }, '') : j; }  throw new SyntaxError('JSON.parse'); }; } } ());
    this.JSON = JSON;

    String.prototype.trim = function() { return this.replace( /^\s+|\s+$/g , ''); };
    
    this.logit = function(msg) {
        var now = new Date();
        GM_log(this.koc.getServerId() + ' @ ' + now.toTimeString().substring(0, 8) + '.' + now.getMilliseconds() + ': ' + msg);
    };

    var tools = {

        getElement: function (locator, logError) {
            //TODO: implement functionality of Selenium element locators
            // http://release.seleniumhq.org/selenium-core/1.0/reference.html
            if (locator.search('//') == 0) return this.getElementByXPath(locator, logError);
            if (locator.search('xpath=') == 0) return this.getElementByXPath(locator.substr(6, locator.length - 6), logError);
            //TODO: if (locator.search('css=') == 0) return this.getElementByCssPath(locator.substr(4, locator.length - 4), logError);
            //TODO: if (locator.search('document.') == 0) return this.getElementByDom(locator.substr(4, locator.length - 4), logError);
            //TODO: if (locator.search('dom=') == 0) return this.getElementByDom(locator.substr(4, locator.length - 4), logError);
            if (locator.search('id=') == 0) return this.getElementById(locator, logError);
            //TODO: if (locator.search('link=') == 0) return document.getElementByLink(locator);
            //TODO: if (locator.search('identifier=') == 0) ...
            var elmt = ById(locator); if (elmt != null) return elmt;
            elmt=document.getElementsByName(locator);
            if (logError && !elmt) GM_log("WARNING: locator not found!" + "\n\tlocator: " + locator + "\n\tUniqueID: {04003277-0D38-4D60-996B-7F0EA5AA04E9}" + (typeof logError !== "boolean" ? "\n\tTag: " + logError : ""));
            return elmt;
        },

        //used by getElement
        /*private*/ getElementById: function (locator, logError) {
            var elmt = ById(locator);
            if (logError && !elmt) GM_log("WARNING: locator not found!" + "\n\tlocator: " + locator + "\n\tUniqueID: {4DFB36F4-F2D6-4982-87E6-8A2A8957E74E}" + (typeof logError !== "boolean" ? "\n\tTag: " + logError : ""));
            return elmt;
        },

        //used by getElement
        /*private*/ getElementByXPath: function (expr, logError) {
            var d = document;
            var elmFirstResult = d.evaluate(expr, document,
                null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
            if (logError && !elmFirstResult) GM_log("WARNING: locator not found!" +"\n\tlocator: " + expr + "\n\tUniqueID: {29D43133-ADCE-473F-A9E1-C61CD1EA005E}" +(typeof logError !== "boolean" ? "\n\tTag: " + logError : ""));
            return elmFirstResult;
        },

        //TODO: getElementByCssPath: function (expr) {},
        //TODO: getElementByDom: function (expr) {},
        //TODO: getElementByLink: function (expr) {},

        getAttribute: function (attributeLocator) {
            var index = attributeLocator.lastIndexOf('@');
            var locator = attributeLocator.substr(0, index);
            var attribute = attributeLocator.substring(index + 1, attributeLocator.length);
            //alert(locator + " " + attribute);
            var elmt = this.getElement(locator);
            var attrValue = elmt.getAttribute(attribute);
            return attrValue;
        },

        click: function (element) {
            if (element === null || element === undefined) throw "Element not specified!";
            if (typeof (element) == "string") element = this.getElement(element);

            var evt = document.createEvent("MouseEvents");
            evt.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
            var canceled = !element.dispatchEvent(evt);
            //OPTIONAL: if (canceled) { /* A handler called preventDefault */ } else {/* None of the handlers called preventDefault*/}
        },

        uncheck: function (element) {
            if (element === null || element === undefined) throw "Element not specified!";
            if (typeof (element) == "string") element = this.getElement(element);
            if (typeof element.checked == "undefined") return; //Klick-Buttons (input type="button")  Checkboxen(input type="checkbox")  Radio-Buttons(input type="radio") 
            if (!element.checked) return;

            var evt = document.createEvent("MouseEvents");
            evt.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
            var canceled = !element.dispatchEvent(evt);
            //OPTIONAL: if (canceled) { /* A handler called preventDefault */ } else {/* None of the handlers called preventDefault*/}
        },

        check: function (element) {
            if (element === null || element === undefined) throw "Element not specified!";
            if (typeof (element) == "string") element = this.getElement(element);
            if (typeof element.checked == "undefined") return;

            if (element.checked) return;

            var evt = document.createEvent("MouseEvents");
            evt.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
            var canceled = !element.dispatchEvent(evt);
            //OPTIONAL: if (canceled) { /* A handler called preventDefault */ } else {/* None of the handlers called preventDefault*/}
        },

        typeKeys: function (element, chars) {
            if (element == null) throw "Element not specified!";
            if (typeof (element) == "string") element = this.getElement(element);
            for (var i = 0; i < chars.length; i++) {
                // Create the key press event.
                var pressEvent = document.createEvent('KeyboardEvent');
                pressEvent.initKeyEvent("keypress", true, true, window, false, false, false, false, 0, chars.charCodeAt(i));

                element.dispatchEvent(pressEvent); // Press the key.
            }
        },

        typeKeys2: function (element, chars) {
            if (element == null) throw "ERROR: typeKeys2 - Element not specified!";
            if (typeof (element) == "string") element = this.getElement(element);

            element.focus();

            for (var i = 0; i < String(chars).length; i++) {
                var c = String(chars).charCodeAt(i);

                var downEvent = document.createEvent('KeyboardEvent');
                downEvent.initKeyEvent("keydown", true, true, window, false, false, false, false, 0, c);
                element.dispatchEvent(downEvent);

                var pressEvent = document.createEvent('KeyboardEvent');
                pressEvent.initKeyEvent("keypress", true, true, window, false, false, false, false, 0, c);
                element.dispatchEvent(pressEvent);

                var upEvent = document.createEvent('KeyboardEvent');
                upEvent.initKeyEvent("keyup", true, true, window, false, false, false, false, 0, c);
                element.dispatchEvent(upEvent);
            }
        },

        type: function (element, chars) {
            if (element == null) throw "Element not specified!";
            if (typeof (element) == "string") element = this.getElement(element);

            if (element.type == 'text') {
                element.focus();
                element.value = chars;
            } else {
                throw "Element not supported!";
            }
        },

        emulateFocus: function (element) {
            emulateEvent(element, "focus");
        },

        emulateEvent: function (element, eventName) {
            var options = extend(defaultOptions, arguments[2] || {});
            var oEvent, eventType = null;
            for (var name in eventMatchers) {
                if (eventMatchers[name].test(eventName)) { eventType = name; break; }
            }
            if (!eventType) throw new SyntaxError('Only HTMLEvents and MouseEvents interfaces are supported');
            if (document.createEvent) {
                oEvent = document.createEvent(eventType);
                if (eventType == 'HTMLEvents') {
                    oEvent.initEvent(eventName, options.bubbles, options.cancelable);
                } else {
                    oEvent.initMouseEvent(eventName, options.bubbles, options.cancelable, document.defaultView,
                         options.button, options.pointerX, options.pointerY, options.pointerX, options.pointerY,
                         options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, element);
                }
                element.dispatchEvent(oEvent);
            } else {
                options.clientX = options.pointerX;
                options.clientY = options.pointerY;
                var evt = document.createEventObject();
                oEvent = extend(evt, options);
                element.fireEvent('on' + eventName, oEvent);
            }
            return element;
        },

        extend: function (destination, source) {
            for (var property in source) destination[property] = source[property];
            return destination;
        },

        eventMatchers: {
            'HTMLEvents': /^(?:load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll)$/,
            'MouseEvents': /^(?:click|dblclick|mouse(?:down|up|over|move|out))$/
        },
        defaultOptions: { pointerX: 0, pointerY: 0, button: 0, ctrlKey: false, altKey: false, shiftKey: false, metaKey: false, bubbles: true, cancelable: true },

        douW: function (func, executeByEmbed) {
            if (this.isChrome || executeByEmbed) {
                var scr = document.createElement('script');
                scr.innerHTML = func;
                document.body.appendChild(scr);
            } else {
                try {
                    eval("uW." + func);
                } catch (error) {
                    logit("Bei DouW hat JavaScript ein fehler gefunden! Meldung: " + error.description);
                }
            }
        },

        findParentByTagName: function (elmt, tagName, findSelf) {
            if (findSelf && elmt.tagName.toLowerCase() == tagName.toLowerCase()) return elmt;
            var t = elmt;
            while (t) {
                try { t = t.parentNode; } catch (ex) { return null; };
                if (t == null) return null;
                //GM_log(t.tagName);
                if (t.tagName.toLowerCase() == tagName.toLowerCase()) break;
            }
            return t;
        },
        
        typeOf:function(v) {
            if (v == undefined) return 'undefined';
            if (typeof(v) == 'object') {
                if (!v) return 'null';
        //    else if (uW.Object.prototype.toString.apply(v) === '[object Array]')
                else if (v.constructor.toString().indexOf("Array") >= 0 && typeof(v.splice) == 'function') return 'array';
                else return 'object';
            }
            return typeof(v);
        },

        className: "KSX+Tools"
    };
    this.tools = tools;

    var selenium = {
        // a small try to implement some Selenium functionality

        click: function(locator) {
            logit("Selenium: click | " + locator);
            var elmt = tools.getElement(locator);
            if (elmt == null) {
                throw "Element not found (" + locator + ")";
            }
            tools.click(elmt);
        },

        getAttribute: function(attributeLocator) {
            logit("Selenium: getAttribute | " + attributeLocator);
            return tools.getAttribute(attributeLocator);
        },

        getElement: function(locator) {
            logit("Selenium: getElement | " + locator);
            return tools.getElement(locator);
        },

        getElementPresent: function(locator) {
            logit("Selenium: getElementPresent | " + locator);
            return tools.getElement(locator) != null;
        },

        waitforElementPresent: function(locator) {
            logit("Selenium: waitforElementPresent | " + locator);
            var tot = new Date().getTime() + 30000;

            do { //because there is no sleep, this will use 100% CPU :-( 
                if (tools.getElementPresent(locator)) return true;
            } while (new Date().getTime() < tot)
            throw "Timeout on waiting for element!";
        },

        className: "KSX.Selenium"
    };
    this.Selenium = selenium;

    /// <summary> Provides funtions to create HTML elements </summary>
    var builder = {

        /// <summary>Creates a blue button </summary>
        BlueButton: function (caption, onclick, id) {
            var a = document.createElement('a');
            if (typeof (onclick) == "string") { a.setAttribute("onclick", onclick); }
            else if (typeof (onclick) == "function") { a.addEventListener("click", onclick, true); }
            else throw "Invalid argument!";
            a.setAttribute("class", "button20");
            a.setAttribute("href", "#");
            if (id) a.setAttribute("id", id);
            var span = document.createElement('span');
            a.appendChild(span);
            span.appendChild(document.createTextNode(caption));
            return a;
        },

        //TODO: DRAFT same as BlueButton but inline
        BlueButton2: function (caption, onclick, id) {
            var a = document.createElement('a');
            if (typeof (onclick) == "string") { a.setAttribute("onclick", onclick); }
            else if (typeof (onclick) == "function") { a.addEventListener("click", onclick, true); }
            else throw "Invalid argument!";
            a.setAttribute("style", "background:  url('img/button20_cap.png') no-repeat scroll right top  transparent;color: #FFFFFF;cursor: pointer;display: inline; font-size:  12px; font-weight: bold; height: 22px; margin-left: 6px; margin-right:  6px; margin-right: 6px; margin-right: 6px; padding: 2px 8px 5px 0px;  text-decoration: none;");
            if (id) a.setAttribute("id", id);
            var span = document.createElement('span');
            span.setAttribute("style", "background: url('img/button20.png') no-repeat scroll 0 0 transparent; padding: 2px 0 5px 8px;");
            a.appendChild(span);
            span.appendChild(document.createTextNode(caption));
            return a;
        },
        ToolbarButton: function (caption, onclick, id) {
            // TODO KSX replace koc-power-pdx.googlecode.com referrence. (Not allowed in library)
            var checked = IMGCheckboxChecked;
            var unchecked = IMGCheckboxUnchecked;
            var unknown = IMGCheckboxUnknown;
            
            var a = document.createElement('a');
            if (typeof (onclick) == "string") { a.setAttribute("onclick", onclick); }
            else if (typeof (onclick) == "function") { a.addEventListener("click", onclick, true); }
            else throw "Invalid argument!";
            a.setAttribute("style", "background:  url('img/button20_cap.png') no-repeat scroll right top  transparent;color: #DFDFFF;cursor: pointer;display: inline; font-size:  11px; font-weight: bold; height: 22px; margin-right:1px;margin-left:1px;  padding: 2px 8px 5px 0px; text-decoration: none;");
            if (id) a.setAttribute("id", id);
            var span = document.createElement('span');
            span.setAttribute("style", "background: url('img/button20.png') no-repeat scroll 0 0 transparent; line-height: 15px; padding: 2px 0 5px 8px;");
            a.appendChild(span);
            //span.appendChild(document.createTextNode(caption));
            //caption  = caption.replace("${[x]}", "<span style='color:#00FF00;  font-size:16px;  vertical-align:middle;'>"+culang.shrbtnchecked+"</span>"); //?
            caption = caption.replace("${[x]}", "<img alt='checked' src='"+checked+"'/>"); 
            //caption  = caption.replace("${[ ]}", "<span style='color:#FF4444;  font-size:16px;  vertical-align:middle;'>"+culang.shrbtnnotchecked+"</span>");
            caption = caption.replace("${[ ]}", "<img alt='unchecked' src='"+unchecked+"'/>"); 
            //caption  = caption.replace("${[?]}", "<span style='color:#AAAAAA;  font-size:16px;  vertical-align:middle;'>"+culang.shrbtnnotfound+"</span>");
            caption = caption.replace("${[ ]}", "<img alt='unknown' src='"+unknown+"'/>"); 
            
            span.innerHTML = caption;
            return a;
        },

        setToolbarCheckButton: function (button, value) {
            // TODO KSX replace koc-power-pdx.googlecode.com referrence. (Not allowed in library)
            var checked = IMGCheckboxChecked;
            var unchecked = IMGCheckboxUnchecked;
            var unknown = IMGCheckboxUnknown;
            
            var chk = button.childNodes[0].childNodes[button.childNodes[0].childNodes.length-1];
//             if (chk.innerHTML != ""+culang.shrbtnnotfound+"" &&  chk.innerHTML != ""+culang.shrbtnchecked+"" && chk.innerHTML !=  ""+culang.shrbtnnotchecked+"") {
//                GM_log("Invalid CheckButton content! '" + chk.innerHTML + "'");
//                return;
//            }
//            chk.style.color = value === null ? "#AAAAAA" : (value ? "#00FF00" : "#FF4444");
//             chk.innerHTML = value === null ? ""+culang.shrbtnnotfound+"" :  (value ? ""+culang.shrbtnchecked+"" : ""+culang.shrbtnnotchecked+"");
            chk.alt = value === null ? "unknown" : (value ? "checked" : "unchecked");
            chk.src = value === null ? unknown : (value ? checked : unchecked);
        },

        ComTabButton: function (caption, onclick, id) {
            var comTabButtonStyle = 'margin-left: 10px; line-height: 35px;';
            var a = document.createElement('a');
            if (typeof (onclick) == "string") { a.setAttribute("onclick", onclick); }
            else if (typeof (onclick) == "function") { a.addEventListener("click", onclick, true); }
            else throw "Invalid argument!";
            a.setAttribute("style", "text-decoration: none; font-weight:bold; color:#FFF; font-variant:small-caps; ");
            if (id) a.setAttribute("id", id);
            var span = document.createElement('span');
            span.setAttribute("style", comTabButtonStyle);
            a.appendChild(span);
            span.appendChild(document.createTextNode(caption));
            return a;
        },

        /// <summary>Creates a blue tab </summary>
        BlueTab: function (caption, onclick, id) {
            var a = document.createElement('a');
            if (typeof (onclick) == "string") { a.setAttribute("onclick", onclick); }
            else if (typeof (onclick) == "function") { a.addEventListener("click", onclick, true); }
            else throw "Invalid argument!";
            a.setAttribute("class", "tab");
            if (id) a.setAttribute("id", id);
            var span = document.createElement('span');
            a.appendChild(span);
            span.appendChild(document.createTextNode(caption));
            return a;
        },

        /// <summary>Creates a green button </summary>
        GreenButton: function (caption, onclick, id) {
            var a = document.createElement('a');
            if (typeof (onclick) == "string") { a.setAttribute("onclick", onclick); }
            else if (typeof (onclick) == "function") { a.addEventListener("click", onclick, true); }
            else throw "Invalid argument!";
            a.setAttribute("class", "buttonGreen20");
            a.setAttribute("href", "#");
            if (id) a.setAttribute("id", id);
            var span = document.createElement('span');
            a.appendChild(span);
            span.appendChild(document.createTextNode(caption));
            return a;
        },

        Span: function (innerHtml, id) {
            var span = document.createElement('span');
            span.innerHTML = innerHtml;
            if (id) a.setAttribute("id", id);
            return span;
        },

        Element: function (elementName, innerElement) {
            var elmt = document.createElement(elementName);
            if (typeof (innerElement) == "object") elmt.appendChild(innerElement);
            else if (typeof (innerElement) == "string") elmt.innerHTML = innerElement;
            else throw "Invalid Argument!";
            return elmt;
        },

        remove: function (id) {
            var elmt = ById(id);
            if (!elmt) return;
            elmt.parentNode.removeChild(elmt);
        },

        //TODO KSX remove function or KoC dependency!
        showDialog: function (header, content) {
            var template = " \
    <div id='modalBox2' class='modalBox modalBox400 ' style='width: 416px; top: 150px; \
        left: 172px; z-index: 100310;'> \
        <div id='modalInner2' class='modalInner modalInner400'> \
            <div id='modalTitleBar2' class='modalTitleBar modalTitleBar400'> \
                <div id='modalTitle2' class='modalTitle'> \
                    ${Header} \
                </div> \
                <div id='modalControls2' class='modalControls'> \
                    <a  id='modalControlsClose2'  onclick='ksx.Builder.remove(\"modalBox2\");return  false;'><span>&nbsp;</span> </a> \
                </div> \
            </div> \
            <div id='modalContent2' class='modalContent modalContent400'> \
                <div class='kofcalert'> \
                    ${Content} \
                </div> \
                <div class='kofcalertbtn clearfix'> \
                    <a class='button20' onclick='ksx.Builder.remove(\"modalBox2\");return false;'><span>Ok</span> </a> \
                </div> \
            </div> \
        </div> \
    </div>";

            var s = template.replace("${Header}", header).replace("${Content}", content);
            document.body.appendChild(ksx.Builder.Element("div", s));
        },

        className: "KSX.Builder"
    };
    this.Builder = builder;
    
    /// <summary> Provides KoC specific stuff </summary>
    this.koc = {
        _ServerID: null,
        getServerId: function () {
            if (this._ServerID == null) {
                var m = /^[a-zA-Z]+([0-9]+)\./.exec(document.location.hostname);
                if (m)
                    this._ServerId = m[1];
                else
                    this._ServerId = '??';
            }
            return this._ServerId;
        },

        _PlayerName: null,
        getPlayerName: function () {
            ///*DEBUGCODE:*/alert("Calling ksx.koc.getPlayerName");
            if (this._PlayerName == null) {
                var e = tools.getElement("topnavDisplayName");
                ///*DEBUGCODE:*/if (e == null) alert("topnavDisplayName not found!");
                this._PlayerName = e.textContent;
            };
            return this._PlayerName;
        },

        getRallyPointLevel: function (cityId, returnLogicalLevel) {
            if (!cityId) cityId = uW.currentcityid;
            else if (city.indexOf("city") != 0) cityId = "city" + cityId;
            if (!returnLogicalLevel) returnLogicalLevel = false;
            var seed = uW.seed;
            var rallypointLevel = 0;
            for (var o in seed.buildings[cityId]) {
                if (parseInt(seed.buildings[cityId][o][0]) == 12) {
                    rallypointLevel = parseInt(seed.buildings[cityId][o][1]);
                    break;
                }
            }
            if (returnLogicalLevel && rallypointLevel == 11) rallypointLevel = 15;
            return rallypointLevel;
        },

        getCurrentCityId: function () {
            return uW.currentcityid; //"city12345567"
        },

        getCurrentMarchType: function () {
            for (var i = 1; i < 6; i++) if (ById('modal_attack_tab_' + i).className == 'selected') return i;
            return 0;
        },

        // Provides a wrapper for the KoC march popup
        MarchUI: function () {
            try {
                for (var i = 1; i <= 12; i++) {
                    // this.unitIpt? = ksx.tools.getElement("modal_attack_unit_ipt?");
                    var elmt1 = ksx.tools.getElement("modal_attack_unit_ipt" + i, true);
                    this["unitIpt" + i] = elmt1;

                    // this.unitnum? = ksx.tools.getElement("//div[@id='modal_attack_unitlist']/div[@name='?']/div/div[2]/span");
                    var elmt2 = ksx.tools.getElement("//div[@id='modal_attack_unitlist']/div[@name='" + i + "']/div/div[2]/span", true);
                    this["unitnum" + i] = elmt2;
                    
                    this["unitAvail" + i] = Number(elmt2.textContent);
                }

                var map = [
                    ["modal_attack_knight", "knight"],
                    ["modal_attack_target_coords_x", "targetCoordsX"],
                    ["modal_attack_target_coords_y", "targetCoordsY"],
                    ["modal_attack_supplyfilter_checkbox", "supplyfilterCheckbox"],
                    ["modal_attack_gold", "gold"],
                    ["modal_attack_rec1", "food"], // nahrung
                    ["modal_attack_rec2", "wood"],  // holz,
                    ["modal_attack_rec3", "stone"],// stein
                    ["modal_attack_rec4", "ore"],  // erz    
                    ["modal_attack_rec5", "aestone"],  // aetherstein                    
                ];
                for (i = 0; i < map.length; i++) {
                    var elmt = ksx.tools.getElement(map[i][0], true);
                    this[map[i][1]] = elmt;
                }

                this.maxGoldValue = ksx.tools.getElement("modal_attack_rec_max_gold", true).textContent;
                this.maxFoodValue = ksx.tools.getElement("modal_attack_rec_max_rec1", true).textContent;
                this.maxWoodValue = ksx.tools.getElement("modal_attack_rec_max_rec2", true).textContent;
                this.maxStoneValue = ksx.tools.getElement("modal_attack_rec_max_rec3", true).textContent;
                this.maxOreValue = ksx.tools.getElement("modal_attack_rec_max_rec4", true).textContent;
                this.maxAeStoneValue = ksx.tools.getElement("modal_attack_rec_max_rec5", true).textContent;

                this.maxUnit = ksx.tools.getElement("modal_attack_maxunt", true);
                this.maxRes = ksx.tools.getElement("modal_attack_maxres", true);

                this.getMarchType = function () {
                    for (i = 1; i < 6; i++) if (ksx.tools.getElement('modal_attack_tab_' + i, true).className == 'selected') return i;
                    return 0;
                };
            } catch (e) {
                GM_log("ERROR: MarchUI - " + e);
                throw e;
            }

        },

        getCityBuilding: function getCityBuilding(cityId, buildingId) {
            var b = uW.seed.buildings['city' + cityId];
            var ret = { count: 0, maxLevel: 0 };
            for (var i = 1; i < 33; i++) {
                if (b['pos' + i] && b['pos' + i][0] == buildingId) {
                    ++ret.count;
                    if (parseInt(b['pos' + i][1]) > ret.maxLevel)
                        ret.maxLevel = parseInt(b['pos' + i][1]);
                }
            }
            return ret;
        },

        getMyAlliance: function () {
            if (uW.seed.allianceDiplomacies == null || uW.seed.allianceDiplomacies.allianceName == null)
                return [0, ''+culang.mainNone+''];
            else
                return [uW.seed.allianceDiplomacies.allianceId, uW.seed.allianceDiplomacies.allianceName];
        },

        getAllianceId: function () {
            if (uW.seed.allianceDiplomacies == null || uW.seed.allianceDiplomacies.allianceName == null)
                return 0; else return uW.seed.allianceDiplomacies.allianceId;
        },

        getLanguage:function () {
            var t=ksx.tools.getElement("//div[@id='mod_untqueue']/div/span",true).textContent;
            switch (t) {
                case SR.en.koc_TroopActivity:lang='en'; // English  en
                case SR.de.koc_TroopActivity:lang='de'; // German   de
                case SR.fr.koc_TroopActivity:lang='fr'; // French   fr
                case SR.it.koc_TroopActivity:lang='it'; // Italian  it
                case SR.tr.koc_TroopActivity:lang='tr'; // Turkish  tr
                case SR.es.koc_TroopActivity:lang='es'; // Spanish  es
                case SR.se.koc_TroopActivity:lang='se'; // Swedish  se   Svenska
                case SR.nl.koc_TroopActivity:lang='nl'; // Dutch    nl   Nederlands
                default                     : return 'en';
            }
        },
        
        // works with greasmonkey, but not compatible with JScript (VS)
        // get lang (){return this.getLanguage();},
        lang: "en",

        // Power v0.6.6
        AjaxRequest: function (url, opts) {
            var headers = {
                'X-Requested-With': 'XMLHttpRequest',
                'X-Prototype-Version': '1.6.1',
                'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
            };
            var ajax = null;

//            if (DEBUG_TRACE_AJAX) logit("AJAX: " + url + "\n" + inspect(opts, 3, 1));

            if (window.XMLHttpRequest) ajax = new XMLHttpRequest();
            else ajax = new ActiveXObject("Microsoft.XMLHTTP");

            if (opts.method == null || opts.method == '') method = 'GET';
            else method = opts.method.toUpperCase();

            if (method == 'POST') {
                headers['Content-type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
            } else if (method == 'GET') {
                addUrlArgs(url, opts.parameters);
            }

            ajax.onreadystatechange = function() {
                //  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; states 0-4
                if (ajax.readyState == 4) {
                    if (ajax.status >= 200 && ajax.status < 305)
                        if (opts.onSuccess) opts.onSuccess(ajax);
                        else if (opts.onFailure) opts.onFailure(ajax);
                } else {
                    if (opts.onChange) opts.onChange(ajax);
                }
            };
            ajax.open(method, url, true); // always async!

            for (var k in headers) ajax.setRequestHeader(k, headers[k]);
            if (ksx.tools.typeOf(opts.requestHeaders) == 'object') for (var k in opts.requestHeaders) ajax.setRequestHeader(k, opts.requestHeaders[k]);

            if (method == 'POST') {
                var a = [];
                for (k in opts.parameters) a.push(k + '=' + opts.parameters[k]);
                ajax.send(a.join('&'));
            } else {
                ajax.send();
            }
        },

        // Power v0.6.6
        MyAjaxRequest: function (url, o, noRetry) {
//            if (DEBUG_TRACE) logit(" 0 myAjaxRequest: " + url + "\n" + inspect(o, 2, 1));
            var opts = uW.Object.clone(o);
            var wasSuccess = o.onSuccess;
            var wasFailure = o.onFailure;
            var retry = 0;
            var delay = 5;
            var noRetry = noRetry === true ? true : false;
            opts.onSuccess = mySuccess;
            opts.onFailure = myFailure;

//            if (DEBUG_TRACE) logit(" 1a myAjaxRequest: " + url + "\n" + inspect(o, 2, 1));
            new ksx.koc.AjaxRequest(url, opts);
            return;

            function myRetry() {
                ++retry;
                new ksx.koc.AjaxRequest(url, opts);
                delay = delay * 1.25;
            }

            function myFailure() {
                var o = { };
//                if (DEBUG_TRACE) logit("myAjaxRequest.myFailure(): " + inspect(rslt, 1, 1));
                o.ok = false;
                o.errorMsg = "AJAX Communication Failure";
                wasFailure(o);
            }

            function mySuccess(msg) {
                var rslt = eval("(" + msg.responseText + ")");
                var x;
                if (rslt.ok) {
//                    if (DEBUG_TRACE) logit(" 1b myAjaxRequest.mySuccess(): " + inspect(rslt, 1, 1));
                    rslt.errorMsg = null; ///// !!!!!!!!!!!!!  ************
                    if (rslt.updateSeed) uW.update_seed(rslt.updateSeed);
                    wasSuccess(rslt);
                    return;
                }
//                if (DEBUG_TRACE) logit(" 1b myAjaxRequest.mySuccess() !ok : " + inspect(rslt, 3, 1));
                rslt.errorMsg = uW.printLocalError((rslt.error_code || null), (rslt.msg || null), (rslt.feedback || null));
                if ((x = rslt.errorMsg.indexOf('<br><br>')) > 0) rslt.errorMsg = rslt.errorMsg.substr(0, x - 1);
                if (!noRetry && (rslt.error_code == 0 || rslt.error_code == 8 || rslt.error_code == 1 || rslt.error_code == 3)) {
                    dialogRetry(rslt.errorMsg, delay, function() {
                        myRetry();
                    }, function() {
                        wasSuccess(rslt);
                    });
                } else {
                    wasSuccess(rslt);
                }
            }
        },
        
        // Power 0.6.6
        inspect:function (obj, maxLevels, level, doFunctions) {
            var str = '', type, msg;
            if (level == null) level = 0;
            if (maxLevels == null) maxLevels = 1;
            if (maxLevels < 1) return 'Inspect Error: Levels number must be > 0';
            if (obj == null) return 'ERROR: Object is NULL\n';
            var indent = '';
            for (var i = 0; i < level; i++) indent += '  ';
            for (var property in obj) {
                try {
                    type = ksx.tools.typeOf(obj[property]);
                    if (doFunctions == true && (type == 'function')) {
                        str += indent + '(' + type + ') ' + property + "[FUNCTION]\n";
                    } else if (type != 'function') {
                        str += indent + '(' + type + ') ' + property + ((obj[property] == null) ? (': null') : ('')) + ' = ' + obj[property] + "\n";
                    }
                    if ((type == 'object' || type == 'array') && (obj[property] != null) && (level + 1 < maxLevels)) str += inspect(obj[property], maxLevels, level + 1, doFunctions); // recurse
                } catch(err) {
                    // Is there some properties in obj we can't access? Print it red.
                    if (typeof(err) == 'string') msg = err;
                    else if (err.message) msg = err.message;
                    else if (err.description) msg = err.description;
                    else msg = 'Unknown';
                    str += '(Error) ' + property + ': ' + msg + "\n";
                }
            }
            str += "\n";
            return str;
        },
        
        attachXYPaste: function (xId, yId, func) {
            // thx (c) unchanged from [KoC Attack - Deutsch] 
            var x = ById(xId);
            if (!x) {
                GM_log("Buttler: Coordinate box not found!");
                return;
            }
            var attached = x.getAttribute('KOCpasteAttached');
            if (attached) return;
            x.setAttribute('maxlength', '20');

            var onchange = function () {
                var xValue = x.value.trim();
                var xI = /^\s*([0-9]+)[\s|,|-|.]+([0-9]+)/.exec(xValue);
                if (xI) {
                    var y = ById(yId);
                    x.value = xI[1];
                    y.value = xI[2];

                    if (func != undefined) func(xI[0], xI[1]);
                }
            };
            x.setAttribute('KOCpasteAttached', true);
            x.addEventListener('keyup', function () { onchange(); }, false);
            x.addEventListener('change', function () { onchange(); }, false);
        },
        
        className: "ksx.koc"
    };

    this.plugins = {className: "ksx.plugins"};
    this.compatibility = { className: "ksx.compatibility" };
    this.configuration = { className: "ksx.configuration" };
    this.className = "KSX";

    /*DEBUGCODE:*/ this.logit("Current player: " + this.koc.getPlayerName());
};

function ToolbarPlugin(ksx) {

    var macros = {
        // contains all functions, which can be called from toolbar buttons
        
        dumpSeed: function () {
            function dump(obj,path) {
                for (var o in objs) {if(o===obj) return;} objs.push(obj);
                //if(ksxDebug) GM_log("dump " + path);
                
                for (var propName in obj) {
                    var propPath = path + "." + propName;
                    var propValue = obj[propName];
                    
                    if(propValue===null) {
                        s += "" + propPath +" (undefinied): null" + "\n";
                        return;
                    }
                    var propValueType = typeof propValue;
                    switch (propValueType) {
                        case "function":return;
                        case "object": s += "" + propPath +" ("+ propValueType +"): >>" /*+ propValue.toString().substr(0,20)*/ + "\n";break;
                        default:s += "" + propPath +" ("+ propValueType +"): "+ propValue.toString() + "\n";break;
                    }
                
                    if(obj.propertyIsEnumerable(propName) && !(typeof propValue === "string")) {
                        ++ind;dump(propValue,propPath);--ind;
                    }
                }
            }
            var objs = [];
            var ind = 0;
            var s = "";
            dump(uW.seed,"seed");
            //GM_log(s);
        },
        dumpThroneItems: function () {
            function dump(obj,path) {
                for (var o in objs) {if(o===obj) return;} objs.push(obj);
                //if(ksxDebug) GM_log("dump " + path);
                
                for (var propName in obj) {
                    var propPath = path + "." + propName;
                    var propValue = obj[propName];
                    
                    if(propValue===null) {
                        s += "" + propPath +" (undefinied): null" + "\n";
                        return;
                    }
                    var propValueType = typeof propValue;
                    switch (propValueType) {
                        case "function":return;
                        case "object": s += "" + propPath +" ("+ propValueType +"): >>" /*+ propValue.toString().substr(0,20)*/ + "\n";break;
                        default:s += "" + propPath +" ("+ propValueType +"): "+ propValue.toString() + "\n";break;
                    }
                
                    if(obj.propertyIsEnumerable(propName) && !(typeof propValue === "string")) {
                        ++ind;dump(propValue,propPath);--ind;
                    }
                }
            }
            var objs = [];
            var ind = 0;
            var s = "";
            dump(uW.kocThroneItems,"kocThroneItems");
            dump(uW.cm.thronestats.effects,"cm.thronestats.effects");
            dump(uW.cm.thronestats.boosts,"cm.thronestats.boosts");
            
            var sEffects="";
            var aType=[];
            for(var propName in uW.cm.thronestats.effects){
                var effect = uW.cm.thronestats.effects[propName]
                sEffects += "" + propName + "\t" + effect[1] + "\t";
                for (l=0;l<effect["2"].length;l++){
                    type = effect["2"][l];
                    sEffects+=type+","
                }
                sEffects += "\n";
            }
            s+=sEffects;
            
            var item_number=0;
            sItems="";
            for(var n1 in uW.kocThroneItems){
                item_number++;
                item=uW.kocThroneItems[n1];
                item_level    = item.level;
                item_quality  = item.quality;
                item_type     = item.type;
                sItems+=item.name+"\n";
                for (iEffect=1;iEffect<=5;iEffect++) {
                    effect_id       = item.effects["slot"+iEffect].id;
                    effect_name     = uW.cm.thronestats.effects[effect_id]["1"]                    
                    effect_tier     = parseInt(item.effects["slot"+iEffect].tier);
                    p        = uW.cm.thronestats.tiers[effect_id][effect_tier];
                    effect_value    = p.base + ((item_level * item_level + item_level) * p.growth * 0.5);
                    effect_isActive = iEffect<=item_quality;
                    sItems+="\t"+effect_name+" "+effect_value + "%"+"\n";
                }
            }
            s+=sItems;
            
            GM_log(s);
        },

        // opens the new message dialog
        newMessage: function () {
            if(ksxDebug) GM_log("Calling ksx.plugins.Toolbar.macros.newMessage()");
            ksx.tools.douW("modal_messages(); track_chrome_btn('messages_btn'); modal_messages_compose();", true);
        },
        
        // opens the send to alliance dialog
        newMessageAtAlliance: function () {
            if(ksxDebug) GM_log("Calling ksx.plugins.Toolbar.macros.newMessageAtAlliance()");
            var doit = "getMessageWindow(" + ksx.koc.getAllianceId() + ",'Alliance Members','allianceall')";
            //if(ksxDebug) GM_log("douW "+doit);
            ksx.tools.douW(doit, true);
        },
        
        // opens the march dialog
        newMarch: function (marchType, x, y, troops, res) { ksx.plugins.MarchExtender.march.show(marchType, x, y, troops, res); },

        // template: function(){},

        className: "Macros"
    };
    this.macros = macros;

    this.initUI = function (thisPlugin) {
        if (ksxDebug) GM_log("Calling ToolbarPlugin.initUI");

        var fc = document.body.firstChild;

        var tbDiv = document.createElement('div');
        tbDiv.id = "ksx_ToolbarPlugin_ToolbarContainer";
        tbDiv.setAttribute("style", "line-height:  22px; font-size: small; text-align: left; border-bottom: 1px solid  silver; height: 24px; width: 740px; background: none repeat scroll 0% 0%  "+Colors.scToolbarBG+"; ");
        // div.style.width = '772px';
        // div.style.width = '1100px'; //+ Chat Rechts

        var lines = 1;
        for (var i = 0; i < ksx.configuration.toolbar.items.length; i++) {
            var item = ksx.configuration.toolbar.items[i];
            if (!item) continue; //skip empty item
            if (ksxDebug) GM_log("Create toolbar item: " + item);
            switch (item[0].toLowerCase()) {
                case "button": tbDiv.appendChild(ksx.Builder.ToolbarButton(item[2], item[3], item[1])); break;
                case "label": tbDiv.appendChild(ksx.Builder.Span(item[2])); break;
                case "html": tbDiv.appendChild(ksx.Builder.Span(item[1])); break;
                case "linebreak": lines++; tbDiv.appendChild(document.createElement("br")); break; //TODO 
                //                case "popup": break; //TODO              
                case "separator": tbDiv.appendChild(ksx.Builder.Span(" | ")); break;
                default: break;
            }
        }

        tbDiv.style.height = (lines * 22 + 4) + 'px';

        var toolbarsPanelDiv = ById("ksx_ToolbarPlugin_ToolbarsPanel");
        if (toolbarsPanelDiv == null) {
            toolbarsPanelDiv = document.createElement('div');
            toolbarsPanelDiv.id = "ksx_ToolbarPlugin_ToolbarsPanel";
            toolbarsPanelDiv.setAttribute("style", "margin: 1px 0pt 0pt 10px;");
            document.body.insertBefore(toolbarsPanelDiv, document.body.firstChild);
        }

        toolbarsPanelDiv.appendChild(tbDiv);
    };

    this.addToolbarItem = function(item) {
        var div = ksx.tools.getElement("ksx_ToolbarPlugin_ToolbarContainer");
        switch (item[0].toLowerCase()) {
            case "button": div.appendChild(ksx.Builder.BlueButton(item[1], item[2])); break;
            case "label" : div.appendChild(ksx.Builder.Span(item[1])); break;
            case "html"  : div.appendChild(ksx.Builder.Span(item[1])); break;
            default: break;
        }
    };

    this.initUI(this);
}

function PowerToolbarPlugin(ksx) {
    
    this.macros = { className: "PowerToolbarPlugin+Macros" };

    this.atSyncButtonClick = function (evt) {
        if (ksxDebug) GM_log("PowerToolbarPlugin.atSyncButtonClick");

        var t = ksx.tools.findParentByTagName(evt.target, "A", true);
        if (!t) { GM_log("Element for event not found!"); return; }

        var id = t.getAttribute("id");
        if (!id) { GM_log("ERROR: Element has no id!"); return; }
        for (var i = 0; i < ksx.configuration.powertoolbar.syncButtons.length; i++) {
            var cfg = ksx.configuration.powertoolbar.syncButtons[i];
            if (cfg[0] === id) {
                if(cfg[2].substr(0,2)=="M:"){
                    eval(cfg[2].substring(2,cfg[2].length));
                } else {
                    var pbButton = ById(cfg[1]);
                    if (!pbButton) { GM_log("ERROR: Element not found! '" + cfg[1] + "'"); return; }
                    if (ksxDebug) GM_log("Click: " + cfg[1]);
                    ksx.tools.click(pbButton);
                }
                return;
            }
        }
        GM_log("ERROR: Config not found! " + id); return;
    };

    this.updateButtons = function () {
        for (var i = 0; i < ksx.configuration.powertoolbar.syncButtons.length; i++) {
            var cfg = ksx.configuration.powertoolbar.syncButtons[i];
            var ksxpbButton = ById(cfg[0]);
            if (ksxpbButton) {
                if(cfg[1].substr(0,2)=="P:"){
                    b=eval(cfg[1].substring(2,cfg[1].length));
                    ksx.Builder.setToolbarCheckButton(ksxpbButton, b);
                } else {
                    var pbButton = ById(cfg[1]);
                    if (pbButton) {
                        ksx.Builder.setToolbarCheckButton(ksxpbButton, String(pbButton[cfg[2]]).search(cfg[3]) != -1);
                    } else {
                        ksx.Builder.setToolbarCheckButton(ksxpbButton, null);
                    }
                }
            }
        }
    };

    this.initUI = function (thisPlugin) {
        if (ksxDebug) GM_log("Calling PowerToolbarPlugin.initUI");

        var tbDiv = document.createElement('div');
        tbDiv.id = "ksx_PowerToolbarPlugin_ToolbarContainer";
        tbDiv.setAttribute("style", "line-height: 22px; font-size: small; text-align: left;  height: 24px; width: 750px; background: none repeat scroll 0% 0%  "+Colors.scToolbarBG2+";");
        // div.style.width = '772px';
        // div.style.width = '1100px'; //+ Chat Rechts

        var lines = 1;
        for (var i = 0; i < ksx.configuration.powertoolbar.items.length; i++) {
            var item = ksx.configuration.powertoolbar.items[i];
            if (!item) continue; //skip empty item
            if (ksxDebug) GM_log("Create toolbar item: " + item);
            switch (item[0].toLowerCase()) {
                case "button": tbDiv.appendChild(ksx.Builder.ToolbarButton(item[2], item[3], item[1])); break;
                case "label": tbDiv.appendChild(ksx.Builder.Span(item[2])); break;
                case "html": tbDiv.appendChild(ksx.Builder.Span(item[1])); break;
                case "linebreak": lines++; tbDiv.appendChild(document.createElement("br")); break; //TODO 
                //                case "popup": break; //TODO             
                case "separator": tbDiv.appendChild(ksx.Builder.Span(" | ")); break;
                default: break;
            }
        }
        tbDiv.style.height = (lines * 22 + 4) + 'px';

        var toolbarsPanelDiv = ById("ksx_ToolbarPlugin_ToolbarsPanel");
        toolbarsPanelDiv.appendChild(tbDiv);
    };

    this.addToolbarItem = function (item) {
        var div = ksx.tools.getElement("ksx_PowerToolbarPlugin_ToolbarContainer");
        switch (item[0].toLowerCase()) {
            case "button": div.appendChild(ksx.Builder.BlueButton(item[1], item[2])); break;
            case "label": div.appendChild(ksx.Builder.Span(item[1])); break;
            case "html": div.appendChild(ksx.Builder.Span(item[1])); break;
            default: break;
        }
    };

    this.updateTimer = function () {
        ksx.plugins.PowerToolbar.updateButtons();
    };

    this.initSync = function (thisPlugin) {
        for (var i = 0; i < ksx.configuration.powertoolbar.syncButtons.length; i++) {
            var cfg = ksx.configuration.powertoolbar.syncButtons[i];
            //GM_log("Init toolbarsync: " + cfg[0] + " " + cfg[1]);
            var tbButton = ById(cfg[0]);
            if (!tbButton) { GM_log("Element not found! '" + cfg[0] + "'"); }
            if (tbButton) { tbButton.addEventListener("click", thisPlugin.atSyncButtonClick, false); }
        }
    };
    this.className = "PowerToolbarPlugin";
    this.initUI(this);
    this.initSync(this);
    window.setInterval(this.updateTimer, 1000);
}

function ChatMonitorPlugin(ksx) {

    this.infoTokens = SR.getArray("ksx_ChatMonitor_InfoTokens",LangOptions.culang); //Spam vom KoC Power z.B.
    this.attackTokens = SR.getArray("ksx_ChatMonitor_AttackTokens",LangOptions.culang);

    this.hooks = [];

    this.AddHook = function (fnc) {
        this.hooks.push(fnc);
    };

    this.Filter = function (name, tokens, conditions, fnc) {
        this.name = name;
        this.tokens = tokens || [];
        this.conditions = conditions || {};
        this.fnc = fnc;
    };

    this.MsgDivWrapper = function (msgdiv) {
        this.msgdiv = msgdiv;
        this.msgType = "unknown"; this.isAlliance = false; this.isDirect = false; this.isNoAlliance = false; this.isGlobal = false;
        if (msgdiv.getAttribute("class") == "chatwrap clearfix") { this.msgType = "alliance"; this.isAlliance = true; }
        else if (msgdiv.getAttribute("class") == "chatwrap clearfix direct") { this.msgType = "direct"; this.isDirect = true; }
        else if (msgdiv.getAttribute("class") == "chatwrap clearfix noalliance") { this.msgType = "noalliance"; this.isNoAlliance = true; }
        else if (msgdiv.getAttribute("class") == "chatwrap clearfix global") { this.msgType = "global"; this.isGlobal = true; }

        if (!this.isNoAlliance) {
            var playerName = ksx.koc.getPlayerName();
            this.img = msgdiv.childNodes[0];
            this.contentDiv = msgdiv.childNodes[1];
            this.infoDiv = msgdiv.childNodes[1].childNodes[0];
            this.whoA = msgdiv.childNodes[1].childNodes[0].childNodes[0];
            this.saysB = msgdiv.childNodes[1].childNodes[0].childNodes[1];
            this.timeSpan = msgdiv.childNodes[1].childNodes[0].childNodes[3];
            this.clearfixDiv = msgdiv.childNodes[1].childNodes[1];
            this.tx = msgdiv.childNodes[1].childNodes[1].childNodes[0];

            this.isOwnEntry = (this.whoA.textContent == playerName);
            this.msgtxt = this.tx.textContent;
        } else {

        }
    };

    this.filters = [];

    //#region Feature: "Cheat Zeit Korrektur"
    this.chatEntryTimeCorrection = function (chatEntry) {
        //if(ksxDebug) GM_log("enableTimeCorrection: " + enableTimeCorrection);
        if (chatEntry.msgdiv.getAttribute("class") != "chatwrap clearfix" && chatEntry.msgdiv.getAttribute("class") != "chatwrap clearfix direct") return;

        var timeOffset = 9; // TODO read from Config
        var chhmm = ksx.tools.getElement("kochead_time").innerHTML.split(":");
        var cmmm = (60 * chhmm[0]) + (1 * chhmm[1]);

        var timeSpan = chatEntry.msgdiv.childNodes[1].childNodes[0].childNodes[3];
        var hhmm = timeSpan.innerHTML.split(":");
        var hh = (1 * hhmm[0]);
        var mmm = hh * 60 + (1 * hhmm[1]);

        if (Math.abs(cmmm - mmm) > 2) {
            hh += timeOffset; if (hh >= 24) hh -= 24; if (hh < 10) hh = "0" + hh;
            var newTime = hh + ":" + hhmm[1];
            //if(ksxDebug) GM_log("Chat entry: time correction: " + timeSpan.innerHTML + " + Offset " + timeOffset + "h = " + newTime);
            timeSpan.innerHTML = newTime;
        }

    };
    //#endregion
    
    this.scanChatItem = function (chatEntry, filter) {
        /*DEBUGCODE:*/if (ksxDebug) GM_log("ChatMonitorPlugin.scanChatItem()");
        
        var infoStyle ='background-color: #ffffff';
    // if (Options.pdxBackgroundStyle=="pdxKoCOld" /* && Options.pbChatOnRight==true*/ ) {
        // infoStyle = "background: " + ksx.configuration.chat.backgrounds.InfoBackground;
    // }else{
        //  infoStyle = "background-color: "+Colors.SpamBackground+";  -moz-box-shadow: 0 0 3px 2px "+Colors.SpamBackground+"; padding:8px;  margin-Bottom:3px; -moz-border-radius:3px;";
        // funktioniert nicht
        // chatEntry.msgdiv.style.linkColor = '#333 !important'; //TODO PDX do it in style
    // }
        
        var infoTokens = ksx.plugins.ChatMonitor.infoTokens;
//        var attackTokens = ksx.plugins.ChatMonitor.attackTokens; //KSX allways in [Power]

        if (chatEntry.token0 == infoTokens[0]) {
            /*DEBUGCODE:*/if (ksxDebug) GM_log("INFO token found");
            chatEntry.msgdiv.setAttribute("style", infoStyle);
            return true;
        }
//KSX allways in [Power]
//          else if (chatEntry.token0 == attackTokens[0] && chatEntry.isOwnEntry) {
//            /*DEBUGCODE:*///if (ksxDebug) GM_log("ATTACK (own) token found");
//            var ownAttackStyle = "background: " + ksx.configuration.chat.backgrounds.OwnAttackBackground;
//            chatEntry.msgdiv.setAttribute("style", ownAttackStyle);
//            return true;
//        } else if (chatEntry.token0 == attackTokens[0] && !chatEntry.isOwnEntry) {
//            /*DEBUGCODE:*/ //if (ksxDebug) GM_log("ATTACK (alliance) token found");
//          var allyAttackStyle = "background: " + ksx.configuration.chat.backgrounds.AllyAttackBackground;
//            chatEntry.msgdiv.setAttribute("style", allyAttackStyle);
//            return true;
//        }
        return false;
    };

    this.scanAllianceChat = function () {
        /*DEBUGCODE:*/if(ksxDebug) GM_log("ChatMonitorPlugin.scanAllianceChat");

        function setEntryHandled(info) {
            if(typeof(info)!=="unspecified" && info!==null) info=" ("+info+")";
            msgdiv.innerHTML += "<div style='display:none'>Entry processed by ChatMonitorPlugin"+info+"</div>";
        }
// TODO KSX modCommList2 is undefined on startup
        var modCommList2 = ksx.tools.getElement("mod_comm_list2");
        for (var i = 0; i < modCommList2.childNodes.length; i++) {
            var msgdiv = modCommList2.childNodes[i];
            var t=msgdiv.textContent;
            if (t.indexOf("Entry processed by ChatMonitorPlugin")>=0) return; //allready handled item found => exit loop
            var chatEntry = new ksx.plugins.ChatMonitor.MsgDivWrapper(msgdiv);

            if (chatEntry.isNoAlliance) { setEntryHandled(); continue; }

            //#region Feature: "Cheat Zeit Korrektur"
            if (ksx.configuration.chat.enableTimeCorrection) {
                ksx.plugins.ChatMonitor.chatEntryTimeCorrection(chatEntry, null);
            }
            //#endregion

            var token = null; var token0 = null; var filter = null;
            for (var k = 0; k < ksx.plugins.ChatMonitor.filters.length; k++) {
                token = null; token0 = null; filter=null;
                var f = ksx.plugins.ChatMonitor.filters[k];
                // /*DEBUG*/ GM_log("Checking filter: [" + k + "].name: " + f.name);
                for (var j = 0; j < f.tokens.length; j++) {
                    var t = f.tokens[j];
                    var isRegExp = (t.match(/^.*\/[gim]*$/)) != null;
                    //GM_log("\"" + t + "\" is regexp " + isRegExp);
                    if (isRegExp) var tg = t.match(/[gim]*$/);
                    if (isRegExp) t = t.replace(/^\//, "").replace(/\/[gim]*$/, "").replace("\\", "\\\\");
                    //if (isRegExp) GM_log("new RegExp(\"" + (t || "") + "\",\"" + (tg || "") + "\")");
                    if (isRegExp) {
                        var regexp = new RegExp((t || ""), (tg || ""));
                        var match = chatEntry.msgtxt.match(regexp);
                        if (match) {
                            filter=f;
                            token = f.tokens[j];
                            token0 = f.tokens[0];
                            if(ksxDebug) GM_log("match: " + match + " in \"" + chatEntry.msgtxt +"\"");
                        } else {
                            if(ksxDebug) GM_log("No match in \"" + chatEntry.msgtxt + "\"");
                        }
                    } else if (chatEntry.msgtxt.indexOf(t) == 0) {
                        filter = f;
                        token = f.tokens[j];
                        token0 = f.tokens[0];
                    }
//                    if (typeof t === "string" && chatEntry.msgtxt.indexOf(t) == 0) {
//                        token = filter.tokens[j];
//                        token0 = filter.tokens[0];
//                    } else if (t instanceof RegExp && chatEntry.msgtxt.match(t) != null) {
//                        token = filter.tokens[j];
//                        token0 = filter.tokens[0];
//                    }
                    if (token != null) break;
                }
                if (token != null) break;
            }
            if (!token) {
                setEntryHandled();
                //if(thisPlugIn.logFilter) GM_log("No token found. filters: (" + ksx.plugins.ChatMonitor.filters.length + ")"); 
                continue;
            }
            chatEntry.token0 = token0;
            chatEntry.token = token;

            /*DEBUGCODE:*/if (ksxDebug) GM_log("Chat token found: " + chatEntry.token + " isOwnEntry: " + chatEntry.isOwnEntry + " context: " + chatEntry.msgType);

            var entryHandled = false;
            for (var k = 0; k < ksx.plugins.ChatMonitor.filters.length; k++) {
                var f = ksx.plugins.ChatMonitor.filters[k];
                //GM_log("Checking filter: [" + k + "].name: " + f.name + " tokens: " + f.tokens + " isOwnEntry: " + f.conditions.isOwnEntry);
                if (!f.tokens) continue;
                if (f.tokens[0] != token0) continue;
                if (!(typeof f.conditions.isOwnEntry === "undefined") && !(f.conditions.isOwnEntry === null) && f.conditions.isOwnEntry != chatEntry.isOwnEntry) continue;

                entryHandled = f.fnc(chatEntry, f);
                if (entryHandled) { entryHandled = true; setEntryHandled(); return; }
            }

            //special case, no token, must be allways the last
            if (entryHandled && chatEntry.isOwnEntry) {
                (function () {
                    var ownStyle = "color:#FFF; background-color: #333; -moz-box-shadow: 0 0 3px 2px #333; padding:8px; margin-Bottom:3px; -moz-border-radius:3px;";
                    chatEntry.msgdiv.setAttribute("style", ownStyle);
                })();
                setEntryHandled();
                continue;
            }

            setEntryHandled();
        }
    };
    
    var filters = this.filters;
    var Filter = this.Filter;
    filters.push(new Filter('infoTokens', this.infoTokens, {}, this.scanChatItem));
//    filters.push(new Filter('attackTokens', this.attackTokens, {}, this.scanChatItem)); //allways in [Power]

    if (ksxDebug) GM_log("setInterval scanAllianceChat");
    window.setInterval(this.scanAllianceChat, 5000);
}

function ResourceRequestPlugin(ksx) {
    //REQUIRES ChatMonitorPlugin, MarchExtenderPlugin
    var thisPlugin = this;
    this.className = "ResourceRequestPlugin";
    
    var lang = LangOptions.culang;//ksx.koc.lang
    var acknowledgeTokens = SR.getArray("ksx_ResourceRequest_AcknowledgeTokens",lang);
    var requestTokens     = SR.getArray("ksx_ResourceRequest_RequestTokens"    ,lang);
    var deliveryTokens    = SR.getArray("ksx_ResourceRequest_DeliveryTokens"   ,lang);
    var foodLowTokens     = SR.getArray("ksx_ResourceRequest_FoodLowTokens"    ,lang);
    
    var atResourceRequestClick = function () {
        if(ksxDebug) GM_log("ResourceRequestPlugin.atResourceRequestClick");
        //var ksx = uW.ksx; if (ksx == null) throw "ksx is null!";

        var rrDestination = ksx.tools.getElement("rrDestination");
        var rrGoldAmount  = ksx.tools.getElement("rrGoldAmount");
        var rrFoodAmount  = ksx.tools.getElement("rrFoodAmount");
        var rrWoodAmount  = ksx.tools.getElement("rrWoodAmount");
        var rrStoneAmount = ksx.tools.getElement("rrStoneAmount");
        var rrOreAmount   = ksx.tools.getElement("rrOreAmount");
        var rrAetherAmount   = ksx.tools.getElement("rrAetherAmount");

        var req = ksx.plugins.ResourceRequest.CurrentRequest;

        req.Version = "2";
        req.Destination = rrDestination.value.toLowerCase().replace(/;|-|\//, ",").replace("-", ",").replace("/", ",").replace(/\s+/g, "");
        //TODO KSX multilang
        req.Gold = rrGoldAmount.value.toLowerCase().replace(" ", "").replace("mille", "M").replace("kk", "M").replace("mio", "M").replace("mrd", "G");
        req.Food = rrFoodAmount.value.toLowerCase().replace(" ", "").replace("mille", "M").replace("kk", "M").replace("mio", "M").replace("mrd", "G");
        req.Wood = rrWoodAmount.value.toLowerCase().replace(" ", "").replace("mille", "M").replace("kk", "M").replace("mio", "M").replace("mrd", "G");
        req.Stone = rrStoneAmount.value.toLowerCase().replace(" ", "").replace("mille", "M").replace("kk", "M").replace("mio", "M").replace("mrd", "G");
        req.Ore = rrOreAmount.value.toLowerCase().replace(" ", "").replace("mille", "M").replace("kk", "M").replace("mio", "M").replace("mrd", "G");
        req.Aether = rrAetherAmount.value.toLowerCase().replace(" ", "").replace("mille", "M").replace("kk", "M").replace("mio", "M").replace("mrd", "G");

        var pers = ksx.JSON.stringify(req);
        //ksx.logit("save: "+pers);
        GM_setValue("ResourceRequest_" + ksx.koc.getServerId(), pers);

        var modCommInput = ksx.tools.getElement("mod_comm_input");
        if (modCommInput == null) { alert("mod_comm_input not found!"); return; }

        // compose ResourceRequest in current language
        // TODO culang.aetherstones
        var sA = "";
        if (req.Gold  .length > 0 && req.Gold   != 0) sA = sA + (sA.length > 0 ? " + " : "") + uW.g_js_strings.commonstr.gold    + " " + req.Gold;
        if (req.Food  .length > 0 && req.Food   != 0) sA = sA + (sA.length > 0 ? " + " : "") + uW.g_js_strings.commonstr.food    + " " + req.Food;
        if (req.Wood  .length > 0 && req.Wood   != 0) sA = sA + (sA.length > 0 ? " + " : "") + uW.g_js_strings.commonstr.wood    + " " + req.Wood;
        if (req.Stone .length > 0 && req.Stone  != 0) sA = sA + (sA.length > 0 ? " + " : "") + uW.g_js_strings.commonstr.stone   + " " + req.Stone;
        if (req.Ore   .length > 0 && req.Ore    != 0) sA = sA + (sA.length > 0 ? " + " : "") + uW.g_js_strings.commonstr.ore     + " " + req.Ore;
        if (req.Aether.length > 0 && req.Aether != 0) sA = sA + (sA.length > 0 ? " + " : "") + uW.g_js_strings.commonstr.aetherstone + " " + req.Aether;
        
        //if(sA.length==0) return;
        var s = "/a " + requestTokens[0] + " " + req.Destination + " " + sA;
        //ksx.logit("Send: "+s);
        
        ksx.tools.type(modCommInput, s);
        ksx.tools.click(modCommInput.nextSibling);

        ksx.tools.getElement("rrContainer").style.display = "none";
        ksx.tools.getElement("//*[@class='comm_body comm_global']").style.display = "";
    };
    this.atResourceRequestClick = atResourceRequestClick;

    this.CurrentRequest = {
        /* [3 - 2012-03-12]
         * NEW field 'Aether'
         */
        Version: "3",
        Destination: "xxx,yyy",
        Gold: "",
        Food: "",
        Wood: "",
        Stone: "",
        Ore: "100M",
        Aether: ""
    };

    var atShowResourceRequest = function () {
        /*DEBUGCODE:*/if(ksxDebug) GM_log("ResourceRequestPlugin.atShowResourceRequest");
        ksx.tools.getElement("rrContainer").style.display = "";
        ksx.tools.getElement("//*[@class='comm_body comm_global']").style.display = "";
    };
    this.atShowResourceRequest = atShowResourceRequest;

    var atCloseResourceRequest = function () {
        /*DEBUGCODE:*/if(ksxDebug) GM_log("ResourceRequestPlugin.atCloseResourceRequest");
        ksx.tools.getElement("rrContainer").style.display = "none";
        ksx.tools.getElement("//*[@class='comm_body comm_global']").style.display = "";
    };
    this.atCloseResourceRequest = atCloseResourceRequest;

    this.initUI = function () {
        /*DEBUGCODE:*//*if (ksxDebug)*/GM_log("ResourceRequestPlugin.initUI");

        var kocCommTabs = ksx.tools.getElement("comm_tabs");
        if (!kocCommTabs) { throw "Element 'comm_tabs' not found! Maybe KoC has some changes."; }
        var modComm = ksx.tools.getElement("//*[@class='mod_comm']");
        if (!modComm) { throw "Element 'mod_comm' not found! Maybe KoC has some changes."; }

        // Append the "Anfrage" Tab resourceRequestButtonTitle
        var btn = ksx.Builder.ComTabButton(culang.ksx_KoCButtler_ResourceRequest_ButtonTitle, "ksx.plugins.ResourceRequest.atShowResourceRequest();return false;");
        kocCommTabs.appendChild(btn);

        //INFO IMGfood, IMGwood, IMGxxx are defined in images.js
        // Append the "Resource-Request" form
        var htmlTemplate ="";
        if (Options.pbChatOnRight==true) {
            htmlTemplate = "<div  id='rrContainer' style='padding: 5px; text-align: center;  -moz-border-radius:3px; border:1px inset #FFF; -moz-box-shadow: 0 0 3px  3px #141516;  background-image:url("+IMGksBG+");  position: relative; top:-80px; height: 290px; margin-left: 50px;  overflow-x: hidden; overflow-y: hidden;  width: 220px;'><table  class=pdxTab style='background-image:url("+IMGksBG+");  color=#FFFFFF; width: 100%'><tr><td><span  style='font-weight: bold; color:  #FFFFFF;'>{%Caption%}</span></td><td  style='text-align: right'><a href='#'  onclick='ksx.plugins.ResourceRequest.atCloseResourceRequest();return  false;'><img src='"+IMGkocClose+"'  alt='X' /></a></td></tr></table><table  style='color:#FFF;' class=pdxTab><tbody><tr  align='center'><td><img src='"+IMGfood30+"'></td><td>{%Amount%}:<input  id='rrFoodAmount' type='text' value='0' maxlength='11'  size='11'></td></tr><tr  align='center'><td><img src='"+IMGwood30+"'></td><td>{%Amount%}:<input  id='rrWoodAmount' type='text' value='0' maxlength='11'  size='11'></td></tr><tr  align='center'><td><img src='"+IMGstone30+"'></td><td>{%Amount%}:<input  id='rrStoneAmount' type='text' value='0' maxlength='11'  size='11'></td></tr><tr  align='center'><td><img src='"+IMGiron30+"'></td><td>{%Amount%}:<input  id='rrOreAmount' type='text' value='0' maxlength='11'  size='11'></td></tr><tr  align='center'><td><img src='"+IMGgold30+"'></td><td>{%Amount%}:<input  id='rrGoldAmount' type='text' value='0' maxlength='11'  size='11'></td></tr><tr  align='center'><td><img src='"+IMGastone30+"'></td><td>{%Amount%}:<input  id='rrAetherAmount' type='text' value='0' maxlength='11'  size='11'></td></tr></tbody></table><div  style='margin-bottom: 10px; display: none'><span  id='rrCityTo'><input id='rrTraderTo_0' class='castleBut  castleButNon' type='submit' value='1'><input id='rrTraderTo_1'  class='castleBut castleButNon' type='submit' value='2'><input  id='rrTraderTo_2' class='castleBut castleButNon' type='submit'  value='3'><input id='rrTraderTo_3' class='castleBut castleButNon'  type='submit' value='4'><input id='rrTraderTo_4' class='castleBut  castleButNon' type='submit' value='5'><input id='rrTraderTo_5'  class='castleBut castleButNon' type='submit' value='6'><input  id='rrTraderTo_6' class='castleBut castleButNon' type='submit'  value='7'><span id='rrTraderToCityName' style='display:  inline-block; width: 85px; font-weight:  bold;'></span></span></div><div  style='color:#FFF;'>{%Destination%} (X,Y):<input  id='rrDestination' type='text' maxlength='7'  size='7'></div><br/><span><a class='button20'  onclick='ksx.plugins.ResourceRequest.atResourceRequestClick();return  false;'><span>{%SendRequest%}</span></a></span></div>";
        } else if (Options.pbChatOnRight==false) {
            htmlTemplate = "<div  id='rrContainer' style='padding: 5px; text-align: center;  -moz-border-radius:3px; border:1px inset #FFF; -moz-box-shadow: 0 0 3px  3px #141516;  background-image:url("+IMGksBG+");  position: relative; top:-310px; height: 290px; margin-left: 355px;  overflow-x: hidden; overflow-y: hidden;  width: 220px;  z-index:2000000;'><table class=pdxTab  style='background-image:url("+IMGksBG+");  color=#FFFFFF; width: 100%'><tr><td><span  style='font-weight: bold; color:  #FFFFFF;'>{%Caption%}</span></td><td  style='text-align: right'><a href='#'  onclick='ksx.plugins.ResourceRequest.atCloseResourceRequest();return  false;'><img src='"+IMGkocClose+"'  alt='X' /></a></td></tr></table><table  style='color:#FFF;' class=pdxTab><tbody><tr  align='center'><td><img src='"+IMGfood30+"'></td><td>{%Amount%}:<input  id='rrFoodAmount' type='text' value='0' maxlength='11'  size='11'></td></tr><tr  align='center'><td><img src='"+IMGwood30+"'></td><td>{%Amount%}:<input  id='rrWoodAmount' type='text' value='0' maxlength='11'  size='11'></td></tr><tr  align='center'><td><img src='"+IMGstone30+"'></td><td>{%Amount%}:<input  id='rrStoneAmount' type='text' value='0' maxlength='11'  size='11'></td></tr><tr  align='center'><td><img src='"+IMGiron30+"'></td><td>{%Amount%}:<input  id='rrOreAmount' type='text' value='0' maxlength='11'  size='11'></td></tr><tr  align='center'><td><img src='"+IMGgold30+"'></td><td>{%Amount%}:<input  id='rrGoldAmount' type='text' value='0' maxlength='11'  size='11'></td></tr><tr  align='center'><td><img src='"+IMGastone30+"'></td><td>{%Amount%}:<input  id='rrAetherAmount' type='text' value='0' maxlength='11'  size='11'></td></tr></tbody></table><div  style='margin-bottom: 10px; display: none'><span  id='rrCityTo'><input id='rrTraderTo_0' class='castleBut  castleButNon' type='submit' value='1'><input id='rrTraderTo_1'  class='castleBut castleButNon' type='submit' value='2'><input  id='rrTraderTo_2' class='castleBut castleButNon' type='submit'  value='3'><input id='rrTraderTo_3' class='castleBut castleButNon'  type='submit' value='4'><input id='rrTraderTo_4' class='castleBut  castleButNon' type='submit' value='5'><input id='rrTraderTo_5'  class='castleBut castleButNon' type='submit' value='6'><input  id='rrTraderTo_6' class='castleBut castleButNon' type='submit'  value='7'><span id='rrTraderToCityName' style='display:  inline-block; width: 85px; font-weight:  bold;'></span></span></div><div  style='color:#FFF;'>{%Destination%} (X,Y):<input  id='rrDestination' type='text' maxlength='7'  size='7'></div><br/><span><a class='button20'  onclick='ksx.plugins.ResourceRequest.atResourceRequestClick();return  false;'><span>{%SendRequest%}</span></a></span></div>";
        }
        htmlTemplate=htmlTemplate.replace(/{%Caption%}/g,culang.ksx_KoCButtler_ResourceRequest_UICaption);
        htmlTemplate=htmlTemplate.replace(/{%Destination%}/g,culang.ksx_KoCButtler_ResourceRequest_UIDestination);
        htmlTemplate=htmlTemplate.replace(/{%Amount%}/g,culang.ksx_KoCButtler_ResourceRequest_UIAmount);
        htmlTemplate=htmlTemplate.replace(/{%SendRequest%}/g,culang.ksx_KoCButtler_ResourceRequest_UISendRequest);
        var div = ksx.Builder.Element("div", htmlTemplate);
        div.setAttribute("id", "rrContainer"); //Workaround id from template is not pressent!?
        div.style.display = "none"; // hide on init
        modComm.appendChild(div);

        var s = GM_getValue("ResourceRequest_" + ksx.koc.getServerId());
        // GM_log("Load ResourceRequest:" + s);

        var rrDestination = ksx.tools.getElement("rrDestination");
        var rrGoldAmount = ksx.tools.getElement("rrGoldAmount");
        var rrFoodAmount = ksx.tools.getElement("rrFoodAmount");
        var rrWoodAmount = ksx.tools.getElement("rrWoodAmount");
        var rrStoneAmount = ksx.tools.getElement("rrStoneAmount");
        var rrOreAmount = ksx.tools.getElement("rrOreAmount");
        var rrAetherAmount = ksx.tools.getElement("rrAetherAmount");

        if (s != null) {
            var rr = ksx.JSON.parse(s);
            if (ksxDebug) GM_log("Output: " + typeof (rr.ksxVersion));
            if (rr.ksxVersion == "3") {
                rrDestination.value = rr.Destination;
                rrGoldAmount.value = rr.Gold;
                rrFoodAmount.value = rr.Food;
                rrWoodAmount.value = rr.Wood;
                rrStoneAmount.value = rr.Stone;
                rrOreAmount.value = rr.Ore;
                rrAtherAmount.value = rr.Aether;
            } else if (rr.ksxVersion == "2") {
                rrDestination.value = rr.Destination;
                rrGoldAmount.value = rr.Gold;
                rrFoodAmount.value = rr.Food;
                rrWoodAmount.value = rr.Wood;
                rrStoneAmount.value = rr.Stone;
                rrOreAmount.value = rr.Ore;
                rrAtherAmount.value = "";
            } else {
                rrDestination.value = "xxx,yyy";
                rrGoldAmount.value = "";
                rrFoodAmount.value = "";
                rrWoodAmount.value = "";
                rrStoneAmount.value = "";
                rrOreAmount.value = "100M";
                rrAetherAmount.value = "";                
            }
        } else {
            rrDestination.value = "xxx,yyy";
            rrGoldAmount.value = "";
            rrFoodAmount.value = "";
            rrWoodAmount.value = "";
            rrStoneAmount.value = "";
            rrOreAmount.value = "100M";
            rrAetherAmount.value = "";    
        }
    };

    this.atRequestEntry = function(msg, filter) { // requestTokens && !isOwnEntry
        /*DEBUGCODE:*/if (ksxDebug) GM_log("REQUEST token found");
        
        var tReqR = msg.msgtxt;
        var t1R = tReqR.indexOf(msg.token);
        tReqR = tReqR.substring(t1R, tReqR.length);
        var t2R = tReqR.indexOf("'");
        if (t2R >= 0) tReqR = tReqR.substr(0, t2R);

        var tAck = tReqR.replace(msg.token, acknowledgeTokens[0]);
        var cA = "var chat = document.getElementById('mod_comm_input'); chat.value = '" + tAck + "';chat.focus(); return false;";

        var actiondiv = document.createElement('div');
        actiondiv.setAttribute("style", "padding: 0 0 0 30px;");
        actiondiv.appendChild(ksx.Builder.GreenButton(culang.ksx_KoCButtler_ResourceRequest_AckButtonTitle, cA));
        msg.msgdiv.appendChild(actiondiv);
        
        var reqStyle = "background: " + ksx.configuration.chat.backgrounds.RequestItemBackground;

        msg.msgdiv.setAttribute("style", reqStyle);
        return true;
    };
    
    this.atOwnAcknowledgeEntry = function (msg, filter) {
        /*DEBUGCODE:*/if (ksxDebug) GM_log("ACKNOWLEDGE token found");
        
        var tAckM = msg.msgtxt;
        var t1A = tAckM.indexOf(msg.token);
        tAckM = tAckM.substring(t1A, tAckM.length);
        var t2A = tAckM.indexOf("'");
        if (t2A >= 0) tAckM = tAckM.substr(0, t2A);

        var goldAmount = 0;
        var foodAmount = 0;
        var woodAmount = 0;
        var stoneAmount = 0;
        var oreAmount = 0;
        var aetherAmount = 0;
        var x; var y;

        var xyPattern = /([0-9]{1,3})\s*(,|-)\s*([0-9]{1,3})/;
        var match = xyPattern.exec(msg.msgtxt);
        if (match === null) return false;
        x = match[1]; y = match[3];

        //TODO KSX multilang
        var pattern = /(Aethersteine|Aetherstein|Aetherstones|Aetherstone|Nahrung|Futter|Lebensmittel|Steaks|Beeren|Food|Erz|Ore|Holz|Wood|Steine|Stein|Stone|Gold)\s*([0-9]+)(mio|mrd|bio|M|G|T|kkkk|kkk|kk|k)/gi;
        while (match = pattern.exec(msg.msgtxt)) {
            var res = match[1];
            var amount = match[2];
            var unit = match[3];
            var amountB;
            switch (unit.toLowerCase()) {
                case "k": amountB = amount * 1000; break;
                case "m": case "mio": case "kk": amountB = amount * 1000000; break;
                case "b": case "bio": case "kkk": amountB = amount * 1000000000; break;
                case "t": case "tio": case "kkkk": amountB = amount * 1000000000000; break;
                default: amountB = amount * 1;
            }

            switch (res.toLowerCase()) {
                case "gold":                                goldAmount   = amountB; break;
                case "nahrung": case "futter": case "lebensmittel": case "steaks": case "beeren": case "food": foodAmount   = amountB; break;
                case "holz": case "wood":                   woodAmount   = amountB; break;
                case "steine": case "stein": case "stone":  stoneAmount  = amountB; break;
                case "erz": case "ore":                     oreAmount    = amountB; break;
                case "aethersteine": case "aetherstones": case "aetherstein": case "aetherstone":   aetherAmount = amountB; break;
                default: break;
            }
        }

        var tDly = tAckM.replace(msg.token, deliveryTokens[0]);
        var cD = " \
                ksx.plugins.MarchExtender.march.show(1, " + x + ", " + y + ", [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [" + goldAmount + ", " + foodAmount + ", " + woodAmount + ", " + stoneAmount + ", " + oreAmount + ", " + aetherAmount + "]); \
                ksx.plugins.MarchExtender.march.transportTools.calculateFastestTroopsForTransport(); \
                var chat = document.getElementById('mod_comm_input'); \
                chat.value = '" + tDly + " / ??min';chat.focus(); \
                return false;";

        var ackStyle = "background: " + ksx.configuration.chat.backgrounds.RequestItemBackground;

        var actiondiv = document.createElement('div');
        actiondiv.setAttribute("style", "padding: 0 0 0 30px;");
        actiondiv.appendChild(ksx.Builder.GreenButton(culang.ksx_KoCButtler_ResourceRequest_DlyButtonTitle, cD));
        msg.msgdiv.appendChild(actiondiv);
        msg.msgdiv.setAttribute("style", ackStyle);

        return true;
    };
    this.atFootLowEntry = function (msg, filter) {
        /*DEBUGCODE:*/ GM_log("FoodLow token found");
        //DE: Mein Heiligtum TEST (777,888) hat zu wenig Nahrung sie reicht noch f?r 1,234,567 (1h 33m) - Verbrauch: -666,777
        //EN: My city Amsterdam (290,506) is low on food. Remaining: 7,629 (2h 16m) Upkeep: -3,348*/

        var allyFoodLowStyle = "background: " + ksx.configuration.chat.backgrounds.RequestItemBackground;
    
        var pattern = [];
        pattern['en'] = /.*\(([0-9]{1,3})\s*(,|-)\s*([0-9]{1,3})\).*\s([0-9,]+)\s\(([0-9\shms ]+)\).*Usage:\s([0-9,-]+)/img;
        //Mein Heiligtum kBraku (599,601) hat zu wenig Nahrung sie reicht noch f?r 106,168,260 (15Std. 47m ) - Verbrauch: -6,724,332
        pattern['de'] = /.*\(([0-9]{1,3})\s*(,|-)\s*([0-9]{1,3})\).*([0-9\s]+)\s\(([0-9\sdhms]+)\).*Produktion:\s([0-9\s-]+)/img;
        //Mon Sanctuaire kBraku (599,601) manque de nourriture 131,484,906 (19h 31m ) - Utilisation: -6,735,717
        pattern['fr'] = /.*\(([0-9]{1,3})\s*(,|-)\s*([0-9]{1,3})\).*\s([0-9,]+)\s\(([0-9\shms ]+)\).*Utilisation:\s([0-9,-]+)/img;
        //Mi Santuario kBraku (599,601) tiene poco alimento 151,976,874 (22h 35m ) - Uso: -6,728,614
        pattern['es'] = /.*\(([0-9]{1,3})\s*(,|-)\s*([0-9]{1,3})\).*\s([0-9,]+)\s\(([0-9\shms ]+)\).*Uso:\s([0-9,-]+)/img;
        //Il mio santuario kBraku (599,601) ha poco cibo, basta ancora per 31,087,035 (4h 37m ) - Consumo: -6,721,511
        pattern['it'] = /.*\(([0-9]{1,3})\s*(,|-)\s*([0-9]{1,3})\).*\s([0-9,]+)\s\(([0-9\shms ]+)\).*Consumo:\s([0-9,-]+)/img;
        //Benim Sehirim kBraku (599,601) in yiyecegi bitmesine kalan s?re 31,647,318 (4saat 42dakika ) - Gider: -6,721,511
        pattern['tr'] = /.*\(([0-9]{1,3})\s*(,|-)\s*([0-9]{1,3})\).*\s([0-9,]+)\s\((.+)\).*Gider:\s([0-9,-]+)/img;
        
        var match = pattern['en'].exec(msg.msgtxt);
        if (match === null) match = pattern['de'].exec(msg.msgtxt);
        if (match === null) match = pattern['fr'].exec(msg.msgtxt);
        if (match === null) match = pattern['es'].exec(msg.msgtxt);
        if (match === null) match = pattern['it'].exec(msg.msgtxt);
        if (match === null) match = pattern['tr'].exec(msg.msgtxt);
        if (match === null) {
            //Uuups
            GM_log("atFootLowEntry not match!? filter: "+filter.name);
            return true;
        }

        var x = match[1];
        var y = match[3];
        var foodleft = match[4].replace(/,/g,"").replace(/\s/g,"");
        var timeleft = match[5];
        var foodconsumption = match[6].replace(/,/g,"").replace(/\s/g,"");

        var foodAmount = -1 * foodconsumption * 8;

        if (ksxDebug) GM_log("FoodLow (Alli): " + x + "," + y + " " + foodleft + " " + timeleft + " " + foodconsumption + "/h -> " + foodAmount);
        

        
        // Bestaetigung
        var tAck = acknowledgeTokens[0] + " " + x + "," + y + " " + uW.g_js_strings.commonstr.food + " " + foodAmount;
        var cA = "var chat = document.getElementById('mod_comm_input'); chat.value = '" + tAck + "';chat.focus(); return false;";

        // Lieferung
        var tDly = deliveryTokens[0] + " " + x + "," + y + " " + uW.g_js_strings.commonstr.food + " " + foodAmount + " /??min";
        var cDt = "\
                ksx.plugins.MarchExtender.march.show(1, " + x + ", " + y + ", [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, " + foodAmount + ", 0, 0, 0]); \
                ksx.plugins.MarchExtender.march.transportTools.calculateFastestTroopsForTransport(); \
                var chat = document.getElementById('mod_comm_input'); chat.value = '" + tDly + "'; \
                return false;";

        var actiondiv = document.createElement('div');
        actiondiv.setAttribute("style", "padding: 0 0 0 30px;");
        actiondiv.appendChild(ksx.Builder.GreenButton(culang.ksx_KoCButtler_ResourceRequest_AckButtonTitle, cA));
        actiondiv.appendChild(ksx.Builder.GreenButton(culang.ksx_KoCButtler_ResourceRequest_ExpressDlyButtonTitle, cDt));
        msg.msgdiv.appendChild(actiondiv);
        msg.msgdiv.setAttribute("style", allyFoodLowStyle);
        return true;
    };
    this.scanChatItem = function (msg, filter) {
        /*DEBUGCODE:*/if (ksxDebug) GM_log("ResourceRequestPlugin.scanChatItem");

        var dlyStyle = "background: " + ksx.configuration.chat.backgrounds.RequestItemBackground;
        
        if (msg.token0 == acknowledgeTokens[0] && !msg.isOwnEntry) {
            /*DEBUGCODE:*/if (ksxDebug) GM_log("ACKNOWLEDGE (alliance) token found");
            msg.msgdiv.setAttribute("style", dlyStyle);
            return true;
        } else if (msg.token0 == deliveryTokens[0] && !msg.isOwnEntry) {
            /*DEBUGCODE:*/if (ksxDebug) GM_log("DELIVERY (alliance) token found");
            msg.msgdiv.setAttribute("style", dlyStyle);
            return true;
            
        } else if (msg.token0 == foodLowTokens[0] && msg.isOwnEntry) {
            /*DEBUGCODE:*/if (ksxDebug) GM_log("FoodLow (own) token found");
            //msg.msgdiv.setAttribute("style", ownFoodLowStyle);
            return true;
        } else if (msg.token0 == foodLowTokens[0] && !msg.isOwnEntry) {
            /*DEBUGCODE:*/if (ksxDebug) GM_log("FoodLow (Alliance) token found");
//            return this.atFootLowEntry(msg, filter);
        }
        return false;
    };



    this.initUI();
    var filters = ksx.plugins.ChatMonitor.filters;
    var Filter = ksx.plugins.ChatMonitor.Filter;
    filters.push(new Filter('REQUEST-Own', requestTokens, { isOwnEntry: true }, this.scanChatItem));
    filters.push(new Filter('REQUEST-Alli', requestTokens, { isOwnEntry: false }, this.atRequestEntry));
    filters.push(new Filter('ACKNOWLEDGE-Own', acknowledgeTokens, { isOwnEntry: true }, this.atOwnAcknowledgeEntry));
    filters.push(new Filter('ACKNOWLEDGE-Alli', acknowledgeTokens, { isOwnEntry: false }, this.scanChatItem));
    filters.push(new Filter('DELIVERY', deliveryTokens, {}, this.scanChatItem));
    //filters.push(new Filter('FoodLow-Own', foodLowTokens, { isOwnEntry: true }, this.scanChatItem)); 
    filters.push(new Filter('FoodLow-Own', foodLowTokens, { isOwnEntry: true }, this.atFootLowEntry));
    filters.push(new Filter('FoodLow-Alli', foodLowTokens, { isOwnEntry: false }, this.atFootLowEntry));
};

function MarchExtenderPlugin(ksx) {
// 2012-05-05 FIX catapult not filled
// 2012-05-02 FIX textbox not filled out. -> usage of typeKeys2 changed.
// 2012-04-16 FIX Repeat Scout allways 0
// 2011-09-19 FIX NaN
// 2011-10-28 FIX troops not filled if any toop type was not available
// 2011-10-28 FIX troops not filled if march type was not 'repeat'

    var thisPlugin = this;
 
    this.march = {
        type: 0,
        ui: null,

        transportTools: {
            uiElement: null,
            uiElementId: "ksxTransportTools",

            hide: function () {
                if(ksxDebug) GM_log("MarchExtenderPlugin.march.transportTools.hide()");
                uiElement = ksx.tools.getElement("ksxTransportTools");
                if (!uiElement) return;
                uiElement.style.display = "none";
            },

            show: function (visible) {
                if (ksxDebug) GM_log("MarchExtenderPlugin.march.transportTools.show(" + Boolean(visible) + ")");
                var t = ksx.plugins.MarchExtender.march.transportTools;
                uiElement = ById(t.uiElementId);
                if (!uiElement) uiElement = t.create();
                t.uiElement.style.display = ((visible) ? "" : "none");
            },

            create: function (visible) {
                if (ksxDebug) GM_log("MarchExtenderPlugin.march.transportTools.create(" + Boolean(visible) + ")");
                var t = ksx.plugins.MarchExtender.march.transportTools;
                t.uiElement = ById(t.uiElementId);
                if (t.uiElement) return t.uiElement;
                
                //TODO KSX multilang
                if(typeof(culang.PredefinedTransports)=="undefined") culang.PredefinedTransports=(LangOptions.culang=="de") ? "Vordefinierte Transporte" : "Predefined Transports";
                if(typeof(culang.Training)=="undefined") culang.Training=(LangOptions.culang=="de") ? "Training" : "Training";
                if(typeof(culang.LastUsed)=="undefined") culang.LastUsed=(LangOptions.culang=="de") ? "Zuletzt verwendet" : "Last Used";
                if(typeof(culang.CalculateTroops)=="undefined") culang.CalculateTroops=(LangOptions.culang=="de") ? "Truppen Berechnen" : "Calculate Troops";
                var myString = " \
                    <div class='section' > \
                        <div class='section_title'>Transport Tools</div> \
                        <div class='section_content' > \
                            <a  class='button20' href='#'  onclick='ksx.plugins.MarchExtender.march.transportTools.calculateFastestTroopsForTransport();return  false;'><span>{%CalculateTroops%}</span></a> \
                            <a  class='buttonGreen20' style='display:inline;  position:absolute;margin-left:4px' href='#'  onclick='ksx.plugins.MarchExtender.march.transportTools.calculateFastestTroopsForTransportHelp();return  false;'><span>?</span></a> \
                            <br/><br/> \
                            <span> \
                                {%PredefinedTransports%}:<br/>  \
                                <select   onchange='ksx.plugins.MarchExtender.march.transportTools.atTransportTemplateChanged(this);return  false;' style='width: 307px'> \
                                        <option value=''><option> \
                                    <optgroup label='{%Training%} (4 {%cottages%}/{%stpop%} 18000, {%shrheavycavalry%},VP11,FP10)'> \
                                        <option value='0,1400000,2700000,0,180000'>{%supplytroop%} (18k)</option>     \
                                        <option value='0,1940000,1800000,0,900000'>{%militiaman%} (18k)</option> \
                                        <option value='0,2660000,1800000,0,2700000'>{%scout%} (18k)</option> \
                                        <option value='0,3200000,9000000,0,1800000'>{%pikeman%} (18k)</option>     \
                                        <option value='0,4100000,2700000,0,7200000'>{%swordsman%} (18k)</option> \
                                        <option value='0,2700000,3150000,0,2700000'>{%archer%} (9k)</option> \
                                        <option value='0,6500000,3600000,0,3000000'>{%cavalry%} (6k)</option>     \
                                        <option value='0,6500000,1500000,0,7500000'>{%heavycavalry%} (3000)</option> \
                                        <option value='0,2700000,6750000,0,1575000'>{%supplywagon%} (4500)</option> \
                                        <option value='0,7250000,8100000,0,4860000'>{%ballista%} (2700)</option> \
                                        <option value='0,6500000,9000000,0,2250000'>{%batteringram%} (1500)</option> \
                                        <option value='0,5000000,5000000,8000000,1200000'>{%catapult%} (1000)</option> \
                                    </optgroup> \
                                    <optgroup label='{%Training%} ({%shrheavycavalry%} {%transport%})'> \
                                        <option value='0,4900000,5500000,0,2750000'>{%militiaman%} (55k, {%shrheavycavalry%},VP:9,FP:9)</option> \
                                        <option value='0,8500000,10000000,0,5000000'>{%militiaman%} (100k, {%shrheavycavalry%},VP:11,FP:10)</option> \
                                    </optgroup> \
                                    <optgroup label='{%LastUsed%}'> \
                                        <option value='0,1500000000,0,0,0'>{%food%} 1500mio</option>     \
                                    </optgroup> \
                                </select> \
                                    <a  class='buttonGreen20' style='display:inline;  position:absolute;margin-left:4px' href='#'  onclick='ksx.plugins.MarchExtender.march.transportTools.transportTemplateHelp();return  false;'><span>?</span></a> \
                            </span> \
                        </div> \
                    </div> \
                    ";
                myString=culang.doTranslate(myString);
                var modalAttackTransport = ksx.tools.getElement("modal_attack_transport");
                t.uiElement = ksx.Builder.Element("div", myString);
                t.uiElement.setAttribute("id", t.uiElementId);
                t.uiElement.style.display = ((visible) ? "" : "none");
                modalAttackTransport.parentNode.appendChild(t.uiElement);
                return t.uiElement;
            },

            calculateFastestTroopsForTransport: function () {
                try {
                    if (ksxDebug) GM_log("MarchExtenderPlugin.march.transportTools.calculateFastestTroopsForTransport");
                    ui = new ksx.koc.MarchUI();

                    var fp = uW.seed.tech["tch10"]; // Level Federgewicht-Pulver
                    var maxUnits = Number(ui.maxUnit.textContent);
                    var wagonLoad = 5000;
                    var hKavLoad = 80;
                    var kavLoad = 100;
                    var scoutLoad = 5;
                    var m = 1 * ui.gold.value + 1 * ui.food.value + 1 * ui.wood.value + 1 * ui.stone.value + 1 * ui.ore.value + 5 * ui.aestone.value;

                    var cHKav  = Math.ceil(m / (hKavLoad  * (1 + fp / 10)));
                    var cWagon = Math.ceil(m / (wagonLoad * (1 + fp / 10)));
                    var cKav   = Math.ceil(m / (kavLoad   * (1 + fp / 10)));
                    var cScout = Math.ceil(m / (scoutLoad * (1 + fp / 10)));
                    if (ksxDebug) GM_log("Reset all units");
                    for (var i = 1; i <= 12; i++) {
                        if (!ui["unitIpt" + i]) GM_log("WARNING: calculateFastestTroopsForTransport - Element not specified - unitIpt" + i);
                        //ui["unitIpt" + i].value = 0; ksx.tools.typeKeys2(ui["unitIpt" + i], "0");
                        ui["unitIpt" + i].value = "0"; ksx.tools.typeKeys2(ui["unitIpt" + i], " ");
                    }
                    if (ksxDebug) GM_log("set best unit");
                    if      (cScout < Number(ui.unitnum3.textContent) && cScout < maxUnits) {ui.unitIpt3.value=cScout; ksx.tools.typeKeys2(ui.unitIpt3, " ");}
                    else if (cKav   < Number(ui.unitnum7.textContent) && cKav   < maxUnits) {ui.unitIpt7.value=cKav  ; ksx.tools.typeKeys2(ui.unitIpt7, " ");}
                    else if (cHKav  < Number(ui.unitnum8.textContent) && cHKav  < maxUnits) {ui.unitIpt8.value=cHKav ; ksx.tools.typeKeys2(ui.unitIpt8, " ");}
                    else if (cWagon < Number(ui.unitnum9.textContent) && cWagon < maxUnits) {ui.unitIpt9.value=cWagon; ksx.tools.typeKeys2(ui.unitIpt9, " ");}
                } catch (e) {
                    GM_log("ERROR: MarchExtenderPlugin.calculateFastestTroopsForTransport - " + e);
                    throw e;
                }
            },
            calculateFastestTroopsForTransportHelp: function () {
                ksx.Builder.showDialog("Truppen Berechnen", "Berechnet für die angegebenen Mengen, die schnellste Transportm?glichkeit.");
            },
            transportTemplateHelp: function () {
                ksx.Builder.showDialog("Vordefinierte Transporte", "Enthält ein Liste mit vordefinierte Transporten. Truppentrainingstransporte sind berechnet für 4 Hütten.");
            },

            atTransportTemplateChanged: function (sender) {
                if (ksxDebug) GM_log("MarchExtenderPlugin.march.transportTools.atTransportTemplateChanged(" + sender + ")");
                ui = new ksx.koc.MarchUI();
                var selectedValue = sender.options[sender.selectedIndex].value;
                GM_log("Seleted: \"" + selectedValue + "\"");
                if (selectedValue == "") return;
                var res = selectedValue.split(",");
                if (res.length != 5) {
                    GM_log("ERROR: Buttler configuration! confEntry:\"" + selectedValue + "\"");
                    return;
                }
                ui.gold   .value = res[0]; ksx.tools.typeKeys2(ui.gold   , " ");
                ui.food   .value = res[1]; ksx.tools.typeKeys2(ui.food   , " ");
                ui.wood   .value = res[2]; ksx.tools.typeKeys2(ui.wood   , " ");
                ui.stone  .value = res[3]; ksx.tools.typeKeys2(ui.stone  , " ");
                ui.ore    .value = res[4]; ksx.tools.typeKeys2(ui.ore    , " ");
                ui.aestone.value = res[5]; ksx.tools.typeKeys2(ui.aestone, " ");

                ksx.plugins.MarchExtender.march.transportTools.calculateFastestTroopsForTransport();
            },
            className: "MarchExtenderPlugin+TransportTools"
        },
        
        marchTools: {
            uiElement: null,
            uiElementId: "ksxMarchTools",

            hide: function () {
                if(ksxDebug) GM_log("MarchExtenderPlugin.march.marchTools.hide()");
                var t = ksx.plugins.MarchExtender.march.marchTools;
                uiElement = ById(t.uiElementId);
                if (!uiElement) return;
                uiElement.style.display = "none";
            },

            show: function (visible) {
                if (ksxDebug) GM_log("MarchExtenderPlugin.march.marchTools.show(" + Boolean(visible) + ")");
                var t = ksx.plugins.MarchExtender.march.marchTools;
                uiElement = ById(t.uiElementId);
                if (!uiElement) uiElement = t.create();
                t.uiElement.style.display = ((visible) ? "" : "none");
            },

            create: function (visible) {
                if (ksxDebug) GM_log("MarchExtenderPlugin.march.marchTools.create(" + Boolean(visible) + ")");
                var t = ksx.plugins.MarchExtender.march.marchTools;
                t.uiElement = ById(t.uiElementId);
                if (t.uiElement) return t.uiElement;
                if(typeof(culang.MarchTools)=="undefined") culang.MarchTools=(LangOptions.culang=="de") ? "MARSCH TOOLS" : "MARCH TOOLS";
                if(typeof(culang.SaveAllways)=="undefined") culang.SaveAllways=(LangOptions.culang=="de") ? "Immer Speichern" : "Save Allways";
                if(typeof(culang.SaveMarch)=="undefined") culang.SaveMarch=(LangOptions.culang=="de") ? "Marsch Speichern" : "Save March";
                var myString = " \
                    <div class='section' > \
                        <div class='section_title'>{%MarchTools%}</div> \
                        <div class='section_content' > \
                            <input  id='ksxMarchExtenderMarchDescription' type='text' style='width: 300px'  tooltip='name, description, comment, what ever you want'/> \
                            <a  class='buttonGreen20' style='display:inline;  position:absolute;margin-left:4px' href='#'  onclick='ksx.plugins.MarchExtender.march.marchTools.saveMarchDescriptionHelp();return  false;'><span>?</span></a> \
                            <br/><br/> \
                            <a  class='button20' href='#'  onclick='ksx.plugins.MarchExtender.saveConfig();return  false;'><span>{%SaveMarch%}</span></a> \
                            <input id='ksxMarchExtenderMarchSaveAllways' type='checkbox' /> \
                            <label for='ksxMarchExtenderMarchSaveAllways'>{%SaveAllways%}</label>\
                            <a  class='buttonGreen20' style='display:inline;  position:absolute;margin-left:4px' href='#'  onclick='ksx.plugins.MarchExtender.march.marchTools.saveMarchHelp();return  false;'><span>?</span></a>\
                        </div> \
                    </div> \
                    ";
                myString=culang.doTranslate(myString);
                var modalAttackTransport = ksx.tools.getElement("modal_attack_transport");
                t.uiElement = ksx.Builder.Element("div", myString);
                t.uiElement.setAttribute("id", t.uiElementId);
                t.uiElement.style.display = ((visible) ? "" : "none");
                modalAttackTransport.parentNode.appendChild(t.uiElement);
                
                var saveAllways=ksx.tools.getElement("ksxMarchExtenderMarchSaveAllways");
                saveAllways.checked='checked';
                var marchDescription=ksx.tools.getElement("ksxMarchExtenderMarchDescription");
                marchDescription.text='';
                
                return t.uiElement;
            },

            saveMarchDescriptionHelp: function () {
                //TODO KSX
                //ksx.Builder.showDialog("Vordefinierte  Transporte", "Enthält ein Liste mit vordefinierte Transporten.  Truppentrainingstransporte sind berechnet für 4 Hütten.");
            },            
            saveMarchHelp: function () {
                //TODO KSX
                //ksx.Builder.showDialog("Vordefinierte  Transporte", "Enthält ein Liste mit vordefinierte Transporten.  Truppentrainingstransporte sind berechnet für 4 Hütten.");
            },


            className: "MarchExtenderPlugin+MarchTools"
        },

        show: function (type, x, y, troops, res) {
            // marchType: 1-Transport, 2-Reinforce, 3-Scout, 4-Attack, 5-Reassign
            // x,y
            // troops: array[12]
            // res: array[gold,food,wood,stone,ore]
            if (ksxDebug) GM_log("MarchExtenderPlugin.newMarch(...)");
            if (type === 'repeatlast' || type === 'repeatlast!') {
                var s = GM_getValue("LastMarch_" + ksx.koc.getServerId());
                if (!s) { /*No configuration*/return; };
                var cfg = ksx.JSON.parse(s);
                type = cfg[0];
                if (!x) x = cfg[1]; // param x will overwride cfg value
                if (!y) y = cfg[2]; // param y will overwride cfg value
                troops = cfg[3];
                res = cfg[4];
                if (!type) type = 1/*Transport*/;
            }
            ksx.plugins.MarchExtender.march.modalattack(type, x, y);
            ksx.plugins.MarchExtender.march.fill(type, x, y, troops, res);
        },
        
        modalattack: function (type, x, y) {
            //Workaround for NaN error. open the march with 'attack' und then switch to other tab
            // GM_log("modalattack type:" + type);
            ksx.tools.douW("modal_attack(" + 4 + ", '" + x + "', '" + y + "')", true);
            //add tab handler
            for (var i = 1; i < 6; i++) ById('modal_attack_tab_' + i).addEventListener('click', ksx.plugins.MarchExtender.march.atMarchTypeChanged, false);
            ksx.tools.click("modal_attack_tab_" + type);
            ksx.plugins.MarchExtender.march.type = type;
            if (typeof ksx.plugins.MarchExtender.original_modal_attack === "undefined") ksx.plugins.MarchExtender.march.init(type); //WORKAROUND if hooks not enabled
        },
        
        init: function (type) {
            if (typeof type == "undefined") type = ksx.koc.getCurrentMarchType();

            //add "March" click handler
            ById('btnMarch').addEventListener('click', ksx.plugins.MarchExtender.march.atMarchClicked, false);

            //TODO: create option
            ksx.tools.uncheck("modal_attack_supplyfilter_checkbox");

            //TODO: create option
            ksx.plugins.MarchExtender.march.transportTools.show((type == 1/*Transport*/ || type == 2/*Reinforce*/ || type == 5/*Reassign*/));
            ksx.plugins.MarchExtender.march.marchTools.show(true);
            
            if (!ksx.compatibility.disableMarchXYPaste)
                ksx.koc.attachXYPaste('modal_attack_target_coords_x', 'modal_attack_target_coords_y');
        },
        saveConfig: function () {
            if (ksxDebug) GM_log("MarchExtenderPlugin.saveMarchConfig()");
            var t = ksx.plugins.MarchExtender.march.transportTools;
            ui = new ksx.koc.MarchUI();

            var cfg = [
                ui.getMarchType(),
                ui.targetCoordsX.value, ui.targetCoordsY.value,
                [ui.unitIpt1.value, ui.unitIpt2.value, ui.unitIpt3.value, ui.unitIpt4.value, ui.unitIpt5.value, ui.unitIpt6.value, ui.unitIpt7.value, ui.unitIpt8.value, ui.unitIpt9.value, ui.unitIpt10.value, ui.unitIpt11.value, ui.unitIpt12.value],
                [ui.gold.value, ui.food.value, ui.wood.value, ui.stone.value, ui.ore.value, ui.aestone.value],
                []
            ];

            var s = ksx.JSON.stringify(cfg);
            if (ksxDebug) GM_log("Save 'LastMarch': " + s);
            GM_setValue("LastMarch_" + ksx.koc.getServerId(), s);
        },

        fill: function (type, x, y, troops, res) {
            if (ksxDebug) GM_log("MarchExtenderPlugin.march.fill("+type+","+x+","+y+",["+troops+"],["+res+"])");
            var ui = new ksx.koc.MarchUI();
            if (!type) type = 1;

            if (type == 1/*Transport*/) {
                ksx.tools.uncheck(ui.supplyfilterCheckbox);
            }
            
            for(i=1;i<=12;i++){
                if( (1*ui["unitAvail"+i]) >= troops[i-1] ){
                    ui["unitIpt"+i].value = troops[i-1]; 
                    ksx.tools.typeKeys2(ui["unitIpt"+i], " ");
                } else {
                    GM_log("MarchExtenderPlugin: Not enought units available! type: "+i+", "+ui["unitAvail"+i]+"/"+troops[i-1]);
                }
            }

            ui.targetCoordsX.value = x;
            ui.targetCoordsY.value = y;

            if (type == 1/*Transport*/ || type == 2/*Reinforce*/ || type == 5/*Reassign*/) {
                ui.gold   .value = res[0]; ksx.tools.typeKeys2(ui.gold   , " ");
                ui.food   .value = res[1]; ksx.tools.typeKeys2(ui.food   , " ");
                ui.wood   .value = res[2]; ksx.tools.typeKeys2(ui.wood   , " ");
                ui.stone  .value = res[3]; ksx.tools.typeKeys2(ui.stone  , " ");
                ui.ore    .value = res[4]; ksx.tools.typeKeys2(ui.ore    , " ");
                ui.aestone.value = res[5]; ksx.tools.typeKeys2(ui.aestone, " ");
            }

            if (type == 4/*Attack*/) {
                // ui.attackKnight.selectedIndex = ui.attackKnight.options.length - 1; // select last
            }
        },

        atMarchTypeChanged: function (evt) {
            if (ksxDebug) GM_log("MarchExtenderPlugin.atMarchTypeChanged");
            type = parseInt(evt.target.id.substr(17)); //modal_attack_tab_{marchType}
            var ui = new ksx.koc.MarchUI();

            //TODO create config option
            ksx.plugins.MarchExtender.march.transportTools.show((type == 1/*Transport*/ || type == 2/*Reinforce*/ || type == 5/*Reassign*/));

            if (type == 3/*Scout*/) {
                GM_log("atMarchTypeChanged 3 "+(ui.unitAvail3 >0));
                //TODO create config option    
                setTimeout(function(){
                    var ui = new ksx.koc.MarchUI();
                    var ksx=uWs.ksx;
                    if(ui.unitAvail3 >0){ui.unitIpt3.value = 1; ksx.tools.typeKeys(ui.unitIpt3, " ");}
                },500);
            }
            if (type == 1/*Transport*/) {
                //TODO create config option
                ksx.tools.uncheck(ui.supplyfilterCheckbox);
            }

            switch (type) {
                case 1/*Transport*/: break;
                case 2/*Reinforce*/: break;
                case 3/*Scout    */: break;
                case 4/*Attack   */: break;
                case 5/*Reassign */: break;
                default: break;
            }
        },
        atMarchClicked: function () {
            if (ksxDebug) GM_log("MarchExtenderPlugin.atMarchClicked()");
            var saveAllways=ksx.tools.getElement("ksxMarchExtenderMarchSaveAllways");
            if(saveAllways.checked='checked')
                ksx.plugins.MarchExtender.march.saveConfig();
        },

        className: "MarchExtenderPlugin+March"
    },

    this.unsafeModalAttackHook = function (marchType, x, y) {
        //if(ksxDebug) GM_log("MarchExtenderPlugin.unsafeModalAttackHook(..)");

        /*ORIGINAL CALL*/
        var ret = ksx.plugins.MarchExtender.original_modal_attack(marchType, x, y);
        /*WORKAROUND  [Power]*/
        if (typeof attackDialog_hook === "function") attackDialog_hook(marchType, x, y); // 

        ksx.plugins.MarchExtender.march.init(marchType);
        return ret;
    };
    
    this.unsafeModalAttackCheckHook=function() {
        /*WORKAROUND [Power]*/ 
        if(typeof modalAttack_hook === "function") modalAttack_hook();
        /*NEWCALL*/ 
        ksx.plugins.MarchExtender.saveMarchConfig();
        /*ORIGINALCALL*/
        var ret=ksx.plugins.MarchExtender.original_modal_attack_check();
        return ret;
    };

    this.setHooks = function () {
        if(ksxDebug) GM_log("MarchExtenderPlugin.setHooks");
        if (typeof uW.modal_attack != "function") {/*UUPS*/GM_log("Check KoC: 'modal_attack' not found!");return;}

        thisPlugin.original_modal_attack = uW.modal_attack;
        uW.modal_attack = this.unsafeModalAttackHook;

//      not used! event handler attached instead
//        thisPlugin.original_modal_attack_check = uW.modal_attack_check;
//        uW.modal_attack_check = this.unsafeModalAttackCheckHook;
    };
    
    // @PDX: Schau mal bitte, wenn ich meine hooks setze, funktionieren deine hooks nicht mehr.
    // die WORKAROUNDs rufen als Notl?sung zwar deine Funktionen auf, aber eine sauber L?sung  ist das leider nicht.
    thisPlugin.setHooks();

    if (ksx.plugins.Toolbar != null) {
        var ksxMarchExtenderRepeatOpen = ById("ksxMarchExtenderRepeatOpen");
        if (ksxMarchExtenderRepeatOpen) ksxMarchExtenderRepeatOpen.addEventListener("click", function () { thisPlugin.march.show('repeatlast', '', '', [], []); }, false);
        var ksxMarchExtenderRepeatMarch = ById("ksxMarchExtenderRepeatMarch");
        if (ksxMarchExtenderRepeatMarch) ksxMarchExtenderRepeatOpen.addEventListener("click", function () { thisPlugin.march.show('repeatlast!', '', '', [], []); }, false);
    }
}

function ReportsPlugin(ksx) {
    var thisPlugin = this;
    var t = {
        maxPages: 1, //t.maxPages = ById("pagesSelect").value;
        totalPages: 0
    };

    this.show = function () {
        t.totalPages = 0;
        t.maxPages = 10;
        thisPlugin.searchReports("", 1);
    };

    this.searchReports = function (rslt, pageNum) {
        if (t.totalPages == 0) {
            var params = uW.Object.clone(uW.g_ajaxparams);
            params.pf = 0;
            params.group = "a";
            params.pageNo = 1;

            new ksx.koc.MyAjaxRequest(uW.g_ajaxpath + "ajax/listReports.php" + uW.g_ajaxsuffix, {
                method: "post",
                parameters: params,
                onSuccess: function (rslt) {
                    if (rslt.ok) {
                        GM_log("ReportsPlugin.searchReports.onSuccess OK");
                        t.totalPages = parseInt(rslt['totalPages']);
                    } else {
                        GM_log("ReportsPlugin.searchReports.onSuccess ERR");
                    }
                },
                onFailure: function () {
                    GM_log("ReportsPlugin.searchReports.onFailure");
                }
            }, false);
        }

        if (t.maxPages == 101) t.maxPages = t.totalPages;

        if (parseInt(pageNum) <= t.maxPages) {
            //ById('searchStatus').innerHTML = pageNum;
            GM_log(">>ReportsPlugin.getReports " + pageNum);
            thisPlugin.getReports(pageNum);
        } else {
            //ById('searchStatus').innerHTML = "Fertig!";
            GM_log("ReportsPlugin Fertig");
        }
    };

    this.getReports = function(pageNum) {
//        var t = Tabs.msg;
        var params = uW.Object.clone(uW.g_ajaxparams);
        params.pf = 0;
        params.group = "a";
        params.pageNo = pageNum;

        var req=new ksx.koc.MyAjaxRequest(uW.g_ajaxpath + "ajax/listReports.php" + uW.g_ajaxsuffix, {
            method: "post",
            parameters: params,
            onSuccess: function(rslt) {
                if (rslt.ok) {
                    GM_log("ReportsPlugin.getReports.onSuccess OK");
                    GM_log(">>ReportsPlugin searchInReports " + pageNum);
                    thisPlugin.searchInReports(rslt, pageNum);
                }else {
                    GM_log("ReportsPlugin.getReports.onSuccess ERR");
                }
            },
            onFailure: function () {
                GM_log("ReportsPlugin.getReports.onFailure");
            }
        }, false);
    };

    this.searchInReports = function(rslt, pageNum) {
//        var t = Tabs.msg;

        var myarray = rslt['arReports'];
        var results = ById("ReportResults");

        for (k in myarray) {
            if (getMyAlliance()[0] != myarray[k]['side1AllianceId'] || myarray[k]['marchType'] == 2) {
                t.content += '<TR><TD><A><SPAN onclick="getReport(' + myarray[k]['reportId'] + ',' + myarray[k]['side0TileType'] + ')">'+culang.mainShow+'</span></a></td>';
                t.content += '<TD>' + pageNum + '</td>';
                t.content += '<TD>' + uW.formatDateByUnixTime(myarray[k]['reportUnixTime']) + '</td>';
                t.content += '<TD>';
                if (rslt['arPlayerNames']['g' + myarray[k]['side1PlayerId']] == "M") t.content += 'Lord ';
                if (rslt['arPlayerNames']['g' + myarray[k]['side1PlayerId']] == "F") t.content += 'Lady ';
                t.content += rslt['arPlayerNames']['p' + myarray[k]['side1PlayerId']] + '</td>';
                t.content += '<td>' + pdxkoordlink(myarray[k]['side1XCoord'], myarray[k]['side1YCoord']) + '</td>';
                t.content += '<TD>' + rslt['arAllianceNames']['a' + myarray[k]['side1AllianceId']] + '</td>';
                if (myarray[k]['marchType'] == 2) t.content += '<TD><FONT color="00CC33">'+culang.mainReinforce+'</font></td>';
                if (myarray[k]['marchType'] == 3) t.content += '<TD><FONT color="FF9933">'+culang.mainScout+'</font></td>';
                if (myarray[k]['marchType'] == 4) t.content += '<TD><FONT color="FF0033">'+culang.mainAttack+'</font></td>';
                t.content += '<TD>';
                if (rslt['arPlayerNames']['g' + myarray[k]['side0PlayerId']] == "M") t.content += 'Lord ';
                if (rslt['arPlayerNames']['g' + myarray[k]['side0PlayerId']] == "F") t.content += 'Lady ';
                t.content += rslt['arPlayerNames']['p' + myarray[k]['side0PlayerId']] + '</td>';
                if (myarray[k]['side0TileType'] == 51) t.content += '<TD>Stadt (' + myarray[k]['side0TileLevel'] + ')</td>';
                else t.content += '<TD><FONT color="#909090"> Wild (' + myarray[k]['side0TileLevel'] + ')</font></td>';
                t.content += '<td>' + pdxkoordlink(myarray[k]['side0XCoord'], myarray[k]['side0YCoord']) + '</td>';
            }
        }
        results.innerHTML = t.content;
        pageNum++;
        t.searchReports(rslt, pageNum);
    };
} 

//    
//          ### EOF ###                                                                                              
//       KOC POWER ALL - EMH EDITION | BUTLLER                                                                            
//    
